2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2005-2010 Daniel Braniss <danny@cs.huji.ac.il>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 | $Id: fsm.c,v 2.8 2007/05/19 16:34:21 danny Exp danny $
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
37 #include <sys/param.h>
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <sys/sysctl.h>
42 #include <netinet/in.h>
43 #include <netinet/tcp.h>
44 #include <arpa/inet.h>
45 #include <sys/ioctl.h>
58 #include <dev/iscsi_initiator/iscsi.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));
150 if(connect(soc, res->ai_addr, res->ai_addrlen) == 0)
161 struct timeval timeout;
164 if(setsockopt(sess->soc, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0)
165 fprintf(stderr, "Cannot set socket KEEPALIVE option err=%d %s\n",
166 errno, strerror(errno));
168 if(setsockopt(sess->soc, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) < 0)
169 fprintf(stderr, "Cannot set socket NO delay option err=%d %s\n",
170 errno, strerror(errno));
174 if((setsockopt(sess->soc, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) < 0)
175 || (setsockopt(sess->soc, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0)) {
176 fprintf(stderr, "Cannot set socket timeout to %ld err=%d %s\n",
177 timeout.tv_sec, errno, strerror(errno));
182 int len = sizeof(val);
183 if(getsockopt(sess->soc, SOL_SOCKET, SO_SNDBUF, &val, &len) == 0)
184 fprintf(stderr, "was: SO_SNDBUF=%dK\n", val/1024);
187 if(sess->op->sockbufsize) {
188 val = sess->op->sockbufsize * 1024;
189 if((setsockopt(sess->soc, SOL_SOCKET, SO_SNDBUF, &val, sizeof(val)) < 0)
190 || (setsockopt(sess->soc, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)) < 0)) {
191 fprintf(stderr, "Cannot set socket sndbuf & rcvbuf to %d err=%d %s\n",
192 val, errno, strerror(errno));
196 sess->flags |= SESS_CONNECTED;
200 fprintf(stderr, "errno=%d\n", sv_errno);
207 if((sess->flags & SESS_REDIRECT) == 0) {
208 if(strcmp(op->targetAddress, sess->target.address) != 0) {
209 syslog(LOG_INFO, "reconnecting to original target address");
210 free(op->targetAddress);
211 op->targetAddress = sess->target.address;
212 op->port = sess->target.port;
213 op->targetPortalGroupTag = sess->target.pgt;
217 sleep(5); // for now ...
220 return 0; // terminal error
225 setOptions(isess_t *sess, int flag)
232 bzero(&oop, sizeof(isc_opt_t));
234 if((flag & SESS_FULLFEATURE) == 0) {
235 oop.initiatorName = sess->op->initiatorName;
236 oop.targetAddress = sess->op->targetAddress;
237 if(sess->op->targetName != 0)
238 oop.targetName = sess->op->targetName;
240 oop.maxRecvDataSegmentLength = sess->op->maxRecvDataSegmentLength;
241 oop.maxXmitDataSegmentLength = sess->op->maxXmitDataSegmentLength; // XXX:
242 oop.maxBurstLength = sess->op->maxBurstLength;
243 oop.maxluns = sess->op->maxluns;
247 | turn on digestion only after login
249 if(sess->op->headerDigest != NULL) {
250 sep = strchr(sess->op->headerDigest, ',');
252 oop.headerDigest = sess->op->headerDigest;
253 debug(1, "oop.headerDigest=%s", oop.headerDigest);
255 if(sess->op->dataDigest != NULL) {
256 sep = strchr(sess->op->dataDigest, ',');
258 oop.dataDigest = sess->op->dataDigest;
259 debug(1, "oop.dataDigest=%s", oop.dataDigest);
263 if(ioctl(sess->fd, ISCSISETOPT, &oop)) {
264 perror("ISCSISETOPT");
271 startSession(isess_t *sess)
279 if((sess->flags & SESS_CONNECTED) == 0) {
283 fd = open(iscsidev, O_RDWR);
289 // XXX: this has to go
291 n = sizeof(sess->isid);
292 if(sysctlbyname("net.iscsi_initiator.isid", (void *)sess->isid, (size_t *)&n, 0, 0) != 0)
293 perror("sysctlbyname");
295 if(ioctl(fd, ISCSISETSES, &n)) {
296 perror("ISCSISETSES");
299 asprintf(&dev, "%s%d", iscsidev, n);
300 nfd = open(dev, O_RDWR);
310 if(setOptions(sess, 0) != 0)
314 if(ioctl(sess->fd, ISCSISETSOC, &sess->soc)) {
315 perror("ISCSISETSOC");
327 syslog(LOG_NOTICE, "trapped signal %d", sig);
328 fprintf(stderr, "trapped signal %d\n", sig);
332 currsess->flags |= SESS_DISCONNECT;
336 currsess->flags |= SESS_RECONNECT;
353 if(ioctl(sess->fd, ISCSIGETCAM, &sess->cam) != 0) {
354 syslog(LOG_WARNING, "ISCSIGETCAM failed: %d", errno);
357 debug(1, "nluns=%d", sess->cam.target_nluns);
359 | for now will do this for each lun ...
361 for(n = i = 0; i < sess->cam.target_nluns; i++) {
362 debug(2, "CAM path_id=%d target_id=%d",
363 sess->cam.path_id, sess->cam.target_id);
365 sess->camdev = cam_open_btl(sess->cam.path_id, sess->cam.target_id,
367 if(sess->camdev == NULL) {
368 //syslog(LOG_WARNING, "%s", cam_errbuf);
369 debug(3, "%s", cam_errbuf);
373 cam_path_string(sess->camdev, pathstr, sizeof(pathstr));
374 debug(2, "pathstr=%s", pathstr);
376 ccb = cam_getccb(sess->camdev);
377 CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->crs);
378 ccb->ccb_h.func_code = XPT_REL_SIMQ;
379 ccb->crs.release_flags = RELSIM_ADJUST_OPENINGS;
380 ccb->crs.openings = sess->op->tags;
381 if(cam_send_ccb(sess->camdev, ccb) < 0)
382 debug(2, "%s", cam_errbuf);
384 if((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
385 syslog(LOG_WARNING, "XPT_REL_SIMQ CCB failed");
386 // cam_error_print(sess->camdev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr);
390 syslog(LOG_INFO, "%s tagged openings now %d\n", pathstr, ccb->crs.openings);
393 cam_close_device(sess->camdev);
399 supervise(isess_t *sess)
405 if(strcmp(sess->op->sessionType, "Discovery") == 0) {
406 sess->flags |= SESS_DISCONNECT;
411 printf("ready to go scsi\n");
413 if(setOptions(sess, SESS_FULLFEATURE) != 0)
416 if((sess->flags & SESS_FULLFEATURE) == 0) {
417 if(daemon(0, 1) != 0) {
421 if(sess->op->pidfile != NULL) {
424 pidf = fopen(sess->op->pidfile, "w");
426 fprintf(pidf, "%d\n", getpid());
430 openlog("iscontrol", LOG_CONS|LOG_PERROR|LOG_PID|LOG_NDELAY, LOG_KERN);
431 syslog(LOG_INFO, "running");
434 if(ioctl(sess->fd, ISCSISTART)) {
435 perror("ISCSISTART");
438 if(doCAM(sess) == 0) {
439 syslog(LOG_WARNING, "no device found");
440 ioctl(sess->fd, ISCSISTOP);
446 if(ioctl(sess->fd, ISCSIRESTART)) {
447 perror("ISCSIRESTART");
452 signal(SIGINT, trap);
453 signal(SIGHUP, trap);
454 signal(SIGTERM, trap);
458 if(ioctl(sess->fd, ISCSISIGNAL, &sig)) {
459 perror("ISCSISIGNAL");
462 sess->flags |= SESS_FULLFEATURE;
464 sess->flags &= ~(SESS_REDIRECT | SESS_RECONNECT);
466 printf("iscontrol: supervise starting main loop\n");
468 | the main loop - actually do nothing
469 | all the work is done inside the kernel
471 while((sess->flags & (SESS_REDIRECT|SESS_RECONNECT|SESS_DISCONNECT)) == 0) {
473 // like sending a nop_out?
476 printf("iscontrol: supervise going down\n");
477 syslog(LOG_INFO, "sess flags=%x", sess->flags);
480 if(ioctl(sess->fd, ISCSISIGNAL, &sig)) {
481 perror("ISCSISIGNAL");
484 if(sess->flags & SESS_DISCONNECT) {
485 sess->flags &= ~SESS_FULLFEATURE;
490 if(ioctl(sess->fd, ISCSISTOP, &val)) {
493 sess->flags |= SESS_INITIALLOGIN1;
499 handledDiscoveryResp(isess_t *sess, pdu_t *pp)
511 n = strlen((char *)ptr) + 1;
519 doDiscovery(isess_t *sess)
522 text_req_t *tp = (text_req_t *)&spp.ipdu.bhs;
526 bzero(&spp, sizeof(pdu_t));
527 tp->cmd = ISCSI_TEXT_CMD /*| 0x40 */; // because of a bug in openiscsi-target
529 tp->ttt = 0xffffffff;
530 addText(&spp, "SendTargets=All");
531 return sendPDU(sess, &spp, handledDiscoveryResp);
535 doLogin(isess_t *sess)
537 isc_opt_t *op = sess->op;
542 if(op->chapSecret == NULL && op->tgtChapSecret == NULL)
544 | don't need any security negotiation
545 | or in other words: we don't have any secrets to exchange
547 sess->csg = LON_PHASE;
549 sess->csg = SN_PHASE;
552 sess->tsih = 0; // XXX: no 'reconnect' yet
553 sess->flags &= ~SESS_NEGODONE; // XXX: KLUDGE
555 count = 10; // should be more than enough
557 debug(3, "count=%d csg=%d", count, sess->csg);
558 status = loginPhase(sess);
560 // just in case we get into a loop
562 } while(status == 0 && (sess->csg != FF_PHASE));
564 sess->flags &= ~SESS_INITIALLOGIN;
565 debug(3, "status=%d", status);
568 case 0: // all is ok ...
569 sess->flags |= SESS_LOGGEDIN;
570 if(strcmp(sess->op->sessionType, "Discovery") == 0)
574 case 1: // redirect - temporary/permanent
576 | start from scratch?
578 sess->flags &= ~SESS_NEGODONE;
579 sess->flags |= (SESS_REDIRECT | SESS_INITIALLOGIN1);
580 syslog(LOG_DEBUG, "target sent REDIRECT");
583 case 2: // initiator terminal error
585 case 3: // target terminal error -- could retry ...
587 return T7; // lets try
594 handleLogoutResp(isess_t *sess, pdu_t *pp)
596 if(sess->flags & SESS_DISCONNECT) {
598 if(ioctl(sess->fd, ISCSISTOP, &val)) {
607 startLogout(isess_t *sess)
610 logout_req_t *p = (logout_req_t *)&spp.ipdu.bhs;
612 bzero(&spp, sizeof(pdu_t));
613 p->cmd = ISCSI_LOGOUT_CMD| 0x40;
614 p->reason = BIT(7) | 0;
617 return sendPDU(sess, &spp, handleLogoutResp);
621 inLogout(isess_t *sess)
623 if(sess->flags & SESS_RECONNECT)
629 S1, S2, /*S3,*/ S4, S5, S6, S7, S8
641 -------<-------------+
642 +--------->/ S1 \<----+ |
661 | | |T5 +--------+---------+
662 | | | /T16+-----+------+ |
663 | | | / -+-----+--+ | |
664 | | | / / S7 \ |T12| |
665 | | | / +->\ /<-+ V V
666 | | | / / -+----- -------
667 | | | / /T11 |T10 / S8 \
668 | | V / / V +----+ \ /
669 | | ---+-+- ----+-- | -------
670 | | / S5 \T9 / S6 \<+ ^
671 | +-----\ /--->\ / T14 |
672 | ------- --+----+------+T17
673 +---------------------------+
682 if((sess = calloc(1, sizeof(isess_t))) == NULL) {
683 // boy, is this a bad start ...
684 fprintf(stderr, "no memory!\n");
692 sess->target.address = strdup(op->targetAddress);
693 sess->target.port = op->port;
694 sess->target.pgt = op->targetPortalGroupTag;
696 sess->flags = SESS_INITIALLOGIN | SESS_INITIALLOGIN1;
702 switch(tcpConnect(sess)) {
703 case T1: state = S2; break;
704 default: state = S8; break;
709 switch(startSession(sess)) {
710 case T2: state = S1; break;
711 case T4: state = S4; break;
712 default: state = S8; break;
717 switch(doLogin(sess)) {
718 case T7: state = S1; break;
719 case T5: state = S5; break;
720 default: state = S8; break;
725 switch(supervise(sess)) {
726 case T8: state = S1; break;
727 case T9: state = S6; break;
728 case T11: state = S7; break;
729 case T15: state = S8; break;
730 default: state = S8; break;
735 switch(startLogout(sess)) {
736 case T13: state = S1; break;
737 case T14: state = S6; break;
738 case T16: state = S8; break;
739 default: state = S8; break;
744 switch(inLogout(sess)) {
745 case T18: state = S1; break;
746 case T10: state = S6; break;
747 case T12: state = S7; break;
748 case T16: state = S8; break;
749 default: state = S8; break;
754 // maybe do some clean up?
755 syslog(LOG_INFO, "terminated");