1*2224f3a1Sclaudio /* $OpenBSD: connection.c,v 1.25 2025/01/28 20:41:44 claudio Exp $ */ 2bde1ae23Sclaudio 3bde1ae23Sclaudio /* 4bde1ae23Sclaudio * Copyright (c) 2009 Claudio Jeker <claudio@openbsd.org> 5bde1ae23Sclaudio * 6bde1ae23Sclaudio * Permission to use, copy, modify, and distribute this software for any 7bde1ae23Sclaudio * purpose with or without fee is hereby granted, provided that the above 8bde1ae23Sclaudio * copyright notice and this permission notice appear in all copies. 9bde1ae23Sclaudio * 10bde1ae23Sclaudio * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11bde1ae23Sclaudio * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12bde1ae23Sclaudio * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13bde1ae23Sclaudio * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14bde1ae23Sclaudio * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15bde1ae23Sclaudio * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16bde1ae23Sclaudio * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17bde1ae23Sclaudio */ 18bde1ae23Sclaudio 19bde1ae23Sclaudio #include <sys/types.h> 20bde1ae23Sclaudio #include <sys/queue.h> 21bde1ae23Sclaudio #include <sys/socket.h> 22bde1ae23Sclaudio #include <sys/uio.h> 23bde1ae23Sclaudio 24bde1ae23Sclaudio #include <netinet/in.h> 25eda5eff4Sclaudio #include <netinet/tcp.h> 26bde1ae23Sclaudio 27bde1ae23Sclaudio #include <scsi/iscsi.h> 28bde1ae23Sclaudio 29bde1ae23Sclaudio #include <errno.h> 30bde1ae23Sclaudio #include <event.h> 31bde1ae23Sclaudio #include <stdio.h> 32bde1ae23Sclaudio #include <stdlib.h> 332f43ab01Smmcc #include <string.h> 34bde1ae23Sclaudio #include <unistd.h> 35bde1ae23Sclaudio 36bde1ae23Sclaudio #include "iscsid.h" 37bde1ae23Sclaudio #include "log.h" 38bde1ae23Sclaudio 39bde1ae23Sclaudio void conn_dispatch(int, short, void *); 40bde1ae23Sclaudio void conn_write_dispatch(int, short, void *); 41bde1ae23Sclaudio 42bde1ae23Sclaudio int c_do_connect(struct connection *, enum c_event); 43bde1ae23Sclaudio int c_do_login(struct connection *, enum c_event); 44bde1ae23Sclaudio int c_do_loggedin(struct connection *, enum c_event); 45daed3c83Sclaudio int c_do_req_logout(struct connection *, enum c_event); 46bde1ae23Sclaudio int c_do_logout(struct connection *, enum c_event); 47cb408c6cSclaudio int c_do_loggedout(struct connection *, enum c_event); 48cb408c6cSclaudio int c_do_fail(struct connection *, enum c_event); 49daed3c83Sclaudio int c_do_cleanup(struct connection *, enum c_event); 50bde1ae23Sclaudio 51bde1ae23Sclaudio const char *conn_state(int); 52bde1ae23Sclaudio const char *conn_event(enum c_event); 53bde1ae23Sclaudio 54bde1ae23Sclaudio void 55bde1ae23Sclaudio conn_new(struct session *s, struct connection_config *cc) 56bde1ae23Sclaudio { 57bde1ae23Sclaudio struct connection *c; 58eda5eff4Sclaudio int nodelay = 1; 59bde1ae23Sclaudio 60bde1ae23Sclaudio if (!(c = calloc(1, sizeof(*c)))) 61bde1ae23Sclaudio fatal("session_add_conn"); 62bde1ae23Sclaudio 63bde1ae23Sclaudio c->fd = -1; 64bde1ae23Sclaudio c->state = CONN_FREE; 65bde1ae23Sclaudio c->session = s; 66bde1ae23Sclaudio c->cid = arc4random(); 67bde1ae23Sclaudio c->config = *cc; 684125a3c4Sclaudio c->mine = initiator_conn_defaults; 69aeec9c16Sclaudio if (s->config.HeaderDigest != 0) 704125a3c4Sclaudio c->mine.HeaderDigest = s->config.HeaderDigest; 71aeec9c16Sclaudio if (s->config.DataDigest != 0) 724125a3c4Sclaudio c->mine.DataDigest = s->config.DataDigest; 734125a3c4Sclaudio c->his = iscsi_conn_defaults; 744125a3c4Sclaudio 750908bd10Sclaudio c->sev.sess = s; 760908bd10Sclaudio c->sev.conn = c; 770908bd10Sclaudio evtimer_set(&c->sev.ev, session_fsm_callback, &c->sev); 780908bd10Sclaudio 79bde1ae23Sclaudio TAILQ_INIT(&c->pdu_w); 80bde1ae23Sclaudio TAILQ_INIT(&c->tasks); 81bde1ae23Sclaudio TAILQ_INSERT_TAIL(&s->connections, c, entry); 82bde1ae23Sclaudio 83bde1ae23Sclaudio if (pdu_readbuf_set(&c->prbuf, PDU_READ_SIZE)) { 84bde1ae23Sclaudio log_warn("conn_new"); 85bde1ae23Sclaudio conn_free(c); 86bde1ae23Sclaudio return; 87bde1ae23Sclaudio } 88bde1ae23Sclaudio 89bde1ae23Sclaudio /* create socket */ 90bde1ae23Sclaudio c->fd = socket(c->config.TargetAddr.ss_family, SOCK_STREAM, 0); 91bde1ae23Sclaudio if (c->fd == -1) { 92bde1ae23Sclaudio log_warn("conn_new: socket"); 93bde1ae23Sclaudio conn_free(c); 94bde1ae23Sclaudio return; 95bde1ae23Sclaudio } 96bde1ae23Sclaudio if (socket_setblockmode(c->fd, 1)) { 97bde1ae23Sclaudio log_warn("conn_new: socket_setblockmode"); 98bde1ae23Sclaudio conn_free(c); 99bde1ae23Sclaudio return; 100bde1ae23Sclaudio } 101bde1ae23Sclaudio 102eda5eff4Sclaudio /* try to turn off TCP Nagle */ 103eda5eff4Sclaudio if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, &nodelay, 104eda5eff4Sclaudio sizeof(nodelay)) == -1) 105eda5eff4Sclaudio log_warn("conn_new: setting TCP_NODELAY"); 106eda5eff4Sclaudio 107bde1ae23Sclaudio event_set(&c->ev, c->fd, EV_READ|EV_PERSIST, conn_dispatch, c); 108bde1ae23Sclaudio event_set(&c->wev, c->fd, EV_WRITE, conn_write_dispatch, c); 109bde1ae23Sclaudio 110bde1ae23Sclaudio conn_fsm(c, CONN_EV_CONNECT); 111bde1ae23Sclaudio } 112bde1ae23Sclaudio 113bde1ae23Sclaudio void 114bde1ae23Sclaudio conn_free(struct connection *c) 115bde1ae23Sclaudio { 116daed3c83Sclaudio log_debug("conn_free"); 117daed3c83Sclaudio 118bde1ae23Sclaudio pdu_readbuf_free(&c->prbuf); 119bde1ae23Sclaudio pdu_free_queue(&c->pdu_w); 120bde1ae23Sclaudio 1210908bd10Sclaudio event_del(&c->sev.ev); 122bde1ae23Sclaudio event_del(&c->ev); 123bde1ae23Sclaudio event_del(&c->wev); 124daed3c83Sclaudio if (c->fd != -1) 125bde1ae23Sclaudio close(c->fd); 126bde1ae23Sclaudio 127cb408c6cSclaudio taskq_cleanup(&c->tasks); 128cb408c6cSclaudio 129bde1ae23Sclaudio TAILQ_REMOVE(&c->session->connections, c, entry); 130bde1ae23Sclaudio free(c); 131bde1ae23Sclaudio } 132bde1ae23Sclaudio 133bde1ae23Sclaudio void 134bde1ae23Sclaudio conn_dispatch(int fd, short event, void *arg) 135bde1ae23Sclaudio { 136bde1ae23Sclaudio struct connection *c = arg; 137bde1ae23Sclaudio ssize_t n; 138bde1ae23Sclaudio 139bde1ae23Sclaudio if (!(event & EV_READ)) { 140bde1ae23Sclaudio log_debug("spurious read call"); 141bde1ae23Sclaudio return; 142bde1ae23Sclaudio } 143bde1ae23Sclaudio if ((n = pdu_read(c)) == -1) { 144f9cc11ecSclaudio if (errno == EAGAIN || errno == ENOBUFS || 145f9cc11ecSclaudio errno == EINTR) /* try later */ 146f9cc11ecSclaudio return; 147f9cc11ecSclaudio log_warn("pdu_read"); 148cb408c6cSclaudio conn_fsm(c, CONN_EV_FAIL); 149bde1ae23Sclaudio return; 150bde1ae23Sclaudio } 151bde1ae23Sclaudio if (n == 0) { /* connection closed */ 152cb408c6cSclaudio conn_fsm(c, CONN_EV_CLOSED); 153bde1ae23Sclaudio return; 154bde1ae23Sclaudio } 155bde1ae23Sclaudio 156bde1ae23Sclaudio pdu_parse(c); 157bde1ae23Sclaudio } 158bde1ae23Sclaudio 159bde1ae23Sclaudio void 160bde1ae23Sclaudio conn_write_dispatch(int fd, short event, void *arg) 161bde1ae23Sclaudio { 162bde1ae23Sclaudio struct connection *c = arg; 163bde1ae23Sclaudio ssize_t n; 164bde1ae23Sclaudio int error; 165bde1ae23Sclaudio socklen_t len; 166bde1ae23Sclaudio 167bde1ae23Sclaudio if (!(event & EV_WRITE)) { 168bde1ae23Sclaudio log_debug("spurious write call"); 169bde1ae23Sclaudio return; 170bde1ae23Sclaudio } 171bde1ae23Sclaudio 172bde1ae23Sclaudio switch (c->state) { 173bde1ae23Sclaudio case CONN_XPT_WAIT: 174bde1ae23Sclaudio len = sizeof(error); 175bde1ae23Sclaudio if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, 176bde1ae23Sclaudio &error, &len) == -1 || (errno = error)) { 177daed3c83Sclaudio log_warn("connect to %s failed", 178bde1ae23Sclaudio log_sockaddr(&c->config.TargetAddr)); 179cb408c6cSclaudio conn_fsm(c, CONN_EV_FAIL); 180bde1ae23Sclaudio return; 181bde1ae23Sclaudio } 182bde1ae23Sclaudio conn_fsm(c, CONN_EV_CONNECTED); 183bde1ae23Sclaudio break; 184bde1ae23Sclaudio default: 185bde1ae23Sclaudio if ((n = pdu_write(c)) == -1) { 186cb408c6cSclaudio log_warn("pdu_write"); 187cb408c6cSclaudio conn_fsm(c, CONN_EV_FAIL); 188bde1ae23Sclaudio return; 189bde1ae23Sclaudio } 190bde1ae23Sclaudio if (n == 0) { /* connection closed */ 191cb408c6cSclaudio conn_fsm(c, CONN_EV_CLOSED); 192bde1ae23Sclaudio return; 193bde1ae23Sclaudio } 194bde1ae23Sclaudio 195bde1ae23Sclaudio /* check if there is more to send */ 196bde1ae23Sclaudio if (pdu_pending(c)) 197bde1ae23Sclaudio event_add(&c->wev, NULL); 198bde1ae23Sclaudio } 199bde1ae23Sclaudio } 200bde1ae23Sclaudio 201bde1ae23Sclaudio void 202bde1ae23Sclaudio conn_fail(struct connection *c) 203bde1ae23Sclaudio { 204cb408c6cSclaudio log_debug("conn_fail"); 205bde1ae23Sclaudio conn_fsm(c, CONN_EV_FAIL); 206bde1ae23Sclaudio } 207bde1ae23Sclaudio 208bde1ae23Sclaudio int 209cb408c6cSclaudio conn_task_ready(struct connection *c) 210cb408c6cSclaudio { 21112bd4d7fSclaudio if ((c->state & CONN_RUNNING) && TAILQ_EMPTY(&c->tasks)) 212cb408c6cSclaudio return 1; 213cb408c6cSclaudio return 0; 214cb408c6cSclaudio } 215cb408c6cSclaudio 216cb408c6cSclaudio void 217bde1ae23Sclaudio conn_task_issue(struct connection *c, struct task *t) 218bde1ae23Sclaudio { 219bde1ae23Sclaudio TAILQ_INSERT_TAIL(&c->tasks, t, entry); 220bde1ae23Sclaudio conn_task_schedule(c); 221bde1ae23Sclaudio } 222bde1ae23Sclaudio 223bde1ae23Sclaudio void 224bde1ae23Sclaudio conn_task_schedule(struct connection *c) 225bde1ae23Sclaudio { 226bde1ae23Sclaudio struct task *t = TAILQ_FIRST(&c->tasks); 227bde1ae23Sclaudio struct pdu *p, *np; 228bde1ae23Sclaudio 229bde1ae23Sclaudio if (!t) { 230bde1ae23Sclaudio log_debug("conn_task_schedule: task is hiding"); 231bde1ae23Sclaudio return; 232bde1ae23Sclaudio } 233bde1ae23Sclaudio 234bde1ae23Sclaudio /* move pdus to the write queue */ 235bde1ae23Sclaudio for (p = TAILQ_FIRST(&t->sendq); p != NULL; p = np) { 236bde1ae23Sclaudio np = TAILQ_NEXT(p, entry); 237bde1ae23Sclaudio TAILQ_REMOVE(&t->sendq, p, entry); 238cb408c6cSclaudio conn_pdu_write(c, p); 239bde1ae23Sclaudio } 240186d2732Sclaudio if (t->callback == NULL) { 241186d2732Sclaudio /* no callback, immediate command expecting no answer */ 2426727bd69Sclaudio conn_task_cleanup(c, t); 243186d2732Sclaudio free(t); 244186d2732Sclaudio } 245bde1ae23Sclaudio } 246bde1ae23Sclaudio 247bde1ae23Sclaudio void 2486727bd69Sclaudio conn_task_cleanup(struct connection *c, struct task *t) 2496727bd69Sclaudio { 2506727bd69Sclaudio pdu_free_queue(&t->sendq); 2516727bd69Sclaudio pdu_free_queue(&t->recvq); 2526727bd69Sclaudio /* XXX need some state to know if queued or not */ 2536727bd69Sclaudio if (c) { 2546727bd69Sclaudio TAILQ_REMOVE(&c->tasks, t, entry); 2556727bd69Sclaudio if (!TAILQ_EMPTY(&c->tasks)) 2566727bd69Sclaudio conn_task_schedule(c); 257e1c3b4f8Sclaudio else 258e1c3b4f8Sclaudio session_schedule(c->session); 2596727bd69Sclaudio } 2606727bd69Sclaudio } 2616727bd69Sclaudio 2624125a3c4Sclaudio #define SET_NUM(p, x, v, min, max) \ 2634125a3c4Sclaudio do { \ 2644125a3c4Sclaudio if (!strcmp((p)->key, #v)) { \ 2654125a3c4Sclaudio (x)->his.v = text_to_num((p)->value, (min), (max), &err); \ 2664125a3c4Sclaudio if (err) { \ 2674125a3c4Sclaudio log_warnx("bad param %s=%s: %s", \ 2684125a3c4Sclaudio (p)->key, (p)->value, err); \ 2694125a3c4Sclaudio errors++; \ 2704125a3c4Sclaudio } \ 2714125a3c4Sclaudio } \ 2724125a3c4Sclaudio } while (0) 2734125a3c4Sclaudio 2744125a3c4Sclaudio #define SET_BOOL(p, x, v) \ 2754125a3c4Sclaudio do { \ 2764125a3c4Sclaudio if (!strcmp((p)->key, #v)) { \ 2774125a3c4Sclaudio (x)->his.v = text_to_bool((p)->value, &err); \ 2784125a3c4Sclaudio if (err) { \ 2794125a3c4Sclaudio log_warnx("bad param %s=%s: %s", \ 2804125a3c4Sclaudio (p)->key, (p)->value, err); \ 2814125a3c4Sclaudio errors++; \ 2824125a3c4Sclaudio } \ 283aeec9c16Sclaudio } \ 284aeec9c16Sclaudio } while (0) 285aeec9c16Sclaudio 286aeec9c16Sclaudio #define SET_DIGEST(p, x, v) \ 287aeec9c16Sclaudio do { \ 288aeec9c16Sclaudio if (!strcmp((p)->key, #v)) { \ 289aeec9c16Sclaudio (x)->his.v = text_to_digest((p)->value, &err); \ 290aeec9c16Sclaudio if (err) { \ 291aeec9c16Sclaudio log_warnx("bad param %s=%s: %s", \ 292aeec9c16Sclaudio (p)->key, (p)->value, err); \ 293aeec9c16Sclaudio errors++; \ 294aeec9c16Sclaudio } \ 2954125a3c4Sclaudio } \ 2964125a3c4Sclaudio } while (0) 2974125a3c4Sclaudio 2984125a3c4Sclaudio int 2994125a3c4Sclaudio conn_parse_kvp(struct connection *c, struct kvp *kvp) 3004125a3c4Sclaudio { 3014125a3c4Sclaudio struct kvp *k; 3024125a3c4Sclaudio struct session *s = c->session; 3034125a3c4Sclaudio const char *err; 3044125a3c4Sclaudio int errors = 0; 3054125a3c4Sclaudio 3064125a3c4Sclaudio 3074125a3c4Sclaudio for (k = kvp; k->key; k++) { 308efb506e8Sclaudio log_debug("conn_parse_kvp: %s = %s", k->key, k->value); 3094b539f84Sclaudio /* XXX handle NotUnderstood|Irrelevant|Reject */ 3104125a3c4Sclaudio SET_NUM(k, s, MaxBurstLength, 512, 16777215); 3114125a3c4Sclaudio SET_NUM(k, s, FirstBurstLength, 512, 16777215); 3124125a3c4Sclaudio SET_NUM(k, s, DefaultTime2Wait, 0, 3600); 3134125a3c4Sclaudio SET_NUM(k, s, DefaultTime2Retain, 0, 3600); 3144125a3c4Sclaudio SET_NUM(k, s, MaxOutstandingR2T, 1, 65535); 315efb506e8Sclaudio SET_NUM(k, s, TargetPortalGroupTag, 0, 65535); 3164125a3c4Sclaudio SET_NUM(k, s, MaxConnections, 1, 65535); 3174125a3c4Sclaudio SET_BOOL(k, s, InitialR2T); 3184125a3c4Sclaudio SET_BOOL(k, s, ImmediateData); 3194125a3c4Sclaudio SET_BOOL(k, s, DataPDUInOrder); 3204125a3c4Sclaudio SET_BOOL(k, s, DataSequenceInOrder); 3214125a3c4Sclaudio SET_NUM(k, s, ErrorRecoveryLevel, 0, 2); 3224125a3c4Sclaudio SET_NUM(k, c, MaxRecvDataSegmentLength, 512, 16777215); 323aeec9c16Sclaudio SET_DIGEST(k, c, HeaderDigest); 324aeec9c16Sclaudio SET_DIGEST(k, c, DataDigest); 3254125a3c4Sclaudio } 3264125a3c4Sclaudio 3274125a3c4Sclaudio if (errors) { 3284125a3c4Sclaudio log_warnx("conn_parse_kvp: errors found"); 3294125a3c4Sclaudio return -1; 3304125a3c4Sclaudio } 3314125a3c4Sclaudio return 0; 3324125a3c4Sclaudio } 3334125a3c4Sclaudio 3344125a3c4Sclaudio #undef SET_NUM 3354125a3c4Sclaudio #undef SET_BOOL 336aeec9c16Sclaudio #undef SET_DIGEST 3374125a3c4Sclaudio 338*2224f3a1Sclaudio #define GET_BOOL_P(dst, req, k, src, f) \ 339*2224f3a1Sclaudio do { \ 340*2224f3a1Sclaudio if (f == 0 && !strcmp(req, #k)) { \ 341*2224f3a1Sclaudio (dst)->key = #k; \ 342*2224f3a1Sclaudio (dst)->value = ((src)->mine.k) ? "Yes" : "No"; \ 343*2224f3a1Sclaudio f++; \ 344*2224f3a1Sclaudio } \ 345*2224f3a1Sclaudio } while (0) 346*2224f3a1Sclaudio 347*2224f3a1Sclaudio #define GET_DIGEST_P(dst, req, k, src, f) \ 348*2224f3a1Sclaudio do { \ 349*2224f3a1Sclaudio if (f == 0 && !strcmp(req, #k)) { \ 350*2224f3a1Sclaudio (dst)->key = #k; \ 351*2224f3a1Sclaudio (dst)->value = \ 352*2224f3a1Sclaudio ((src)->mine.k == DIGEST_NONE) ? "None" : "CRC32C,None";\ 353*2224f3a1Sclaudio f++; \ 354*2224f3a1Sclaudio } \ 355*2224f3a1Sclaudio } while (0) 356*2224f3a1Sclaudio 357*2224f3a1Sclaudio #define GET_NUM_P(dst, req, k, src, f, e) \ 358*2224f3a1Sclaudio do { \ 359*2224f3a1Sclaudio if (f == 0 && !strcmp(req, #k)) { \ 360*2224f3a1Sclaudio (dst)->key = #k; \ 361*2224f3a1Sclaudio if (asprintf(&((dst)->value), "%u", (src)->mine.k) == -1)\ 362*2224f3a1Sclaudio e++; \ 363*2224f3a1Sclaudio else \ 364*2224f3a1Sclaudio (dst)->flags |= KVP_VALUE_ALLOCED; \ 365*2224f3a1Sclaudio f++; \ 366*2224f3a1Sclaudio } \ 367*2224f3a1Sclaudio } while (0) 368*2224f3a1Sclaudio 369*2224f3a1Sclaudio #define GET_STR_C(dst, req, k, src, f) \ 370*2224f3a1Sclaudio do { \ 371*2224f3a1Sclaudio if (f == 0 && !strcmp(req, #k)) { \ 372*2224f3a1Sclaudio (dst)->key = #k; \ 373*2224f3a1Sclaudio (dst)->value = (src)->config.k; \ 374*2224f3a1Sclaudio f++; \ 375*2224f3a1Sclaudio } \ 376*2224f3a1Sclaudio } while (0) 377*2224f3a1Sclaudio 378*2224f3a1Sclaudio #define GET_STYPE_C(dst, req, k, src, f) \ 379*2224f3a1Sclaudio do { \ 380*2224f3a1Sclaudio if (f == 0 && !strcmp(req, #k)) { \ 381*2224f3a1Sclaudio (dst)->key = #k; \ 382*2224f3a1Sclaudio (dst)->value = ((src)->config.k == SESSION_TYPE_DISCOVERY)\ 383*2224f3a1Sclaudio ? "Discovery" : "Normal"; \ 384*2224f3a1Sclaudio f++; \ 385*2224f3a1Sclaudio } \ 386*2224f3a1Sclaudio } while (0) 387*2224f3a1Sclaudio 3884125a3c4Sclaudio int 389*2224f3a1Sclaudio kvp_set_from_mine(struct kvp *kvp, const char *key, struct connection *c) 3904125a3c4Sclaudio { 391*2224f3a1Sclaudio int e = 0, f = 0; 3924125a3c4Sclaudio 393*2224f3a1Sclaudio if (kvp->flags & KVP_KEY_ALLOCED) 394*2224f3a1Sclaudio free(kvp->key); 395*2224f3a1Sclaudio kvp->key = NULL; 396*2224f3a1Sclaudio if (kvp->flags & KVP_VALUE_ALLOCED) 397*2224f3a1Sclaudio free(kvp->value); 398*2224f3a1Sclaudio kvp->value = NULL; 399*2224f3a1Sclaudio kvp->flags = 0; 4004125a3c4Sclaudio 401*2224f3a1Sclaudio /* XXX handle at least CHAP */ 402*2224f3a1Sclaudio if (!strcmp(key, "AuthMethod")) { 403*2224f3a1Sclaudio kvp->key = "AuthMethod"; 404*2224f3a1Sclaudio kvp->value = "None"; 405be16f395Sclaudio return 0; 4064125a3c4Sclaudio } 407*2224f3a1Sclaudio GET_DIGEST_P(kvp, key, HeaderDigest, c, f); 408*2224f3a1Sclaudio GET_DIGEST_P(kvp, key, DataDigest, c, f); 409*2224f3a1Sclaudio GET_NUM_P(kvp, key, MaxConnections, c->session, f, e); 410*2224f3a1Sclaudio GET_STR_C(kvp, key, TargetName, c->session, f); 411*2224f3a1Sclaudio GET_STR_C(kvp, key, InitiatorName, c->session, f); 412*2224f3a1Sclaudio GET_BOOL_P(kvp, key, InitialR2T, c->session, f); 413*2224f3a1Sclaudio GET_BOOL_P(kvp, key, ImmediateData, c->session, f); 414*2224f3a1Sclaudio GET_NUM_P(kvp, key, MaxRecvDataSegmentLength, c, f, e); 415*2224f3a1Sclaudio GET_NUM_P(kvp, key, MaxBurstLength, c->session, f, e); 416*2224f3a1Sclaudio GET_NUM_P(kvp, key, FirstBurstLength, c->session, f, e); 417*2224f3a1Sclaudio GET_NUM_P(kvp, key, DefaultTime2Wait, c->session, f, e); 418*2224f3a1Sclaudio GET_NUM_P(kvp, key, DefaultTime2Retain, c->session, f, e); 419*2224f3a1Sclaudio GET_NUM_P(kvp, key, MaxOutstandingR2T, c->session, f, e); 420*2224f3a1Sclaudio GET_BOOL_P(kvp, key, DataPDUInOrder, c->session, f); 421*2224f3a1Sclaudio GET_BOOL_P(kvp, key, DataSequenceInOrder, c->session, f); 422*2224f3a1Sclaudio GET_NUM_P(kvp, key, ErrorRecoveryLevel, c->session, f, e); 423*2224f3a1Sclaudio GET_STYPE_C(kvp, key, SessionType, c->session, f); 424*2224f3a1Sclaudio /* XXX handle TaskReporting */ 425*2224f3a1Sclaudio 426*2224f3a1Sclaudio if (f == 0) { 427*2224f3a1Sclaudio errno = EINVAL; 428*2224f3a1Sclaudio return 1; 429*2224f3a1Sclaudio } 430*2224f3a1Sclaudio 431*2224f3a1Sclaudio return e; 432*2224f3a1Sclaudio } 433*2224f3a1Sclaudio 434*2224f3a1Sclaudio #undef GET_BOOL_P 435*2224f3a1Sclaudio #undef GET_DIGEST_P 436*2224f3a1Sclaudio #undef GET_NUM_P 437*2224f3a1Sclaudio #undef GET_STR_C 438*2224f3a1Sclaudio #undef GET_STYPE_C 4396727bd69Sclaudio 4406727bd69Sclaudio void 441bde1ae23Sclaudio conn_pdu_write(struct connection *c, struct pdu *p) 442bde1ae23Sclaudio { 443bde1ae23Sclaudio struct iscsi_pdu *ipdu; 444bde1ae23Sclaudio 445bde1ae23Sclaudio /* XXX I GUESS THIS SHOULD BE MOVED TO PDU SOMEHOW... */ 446bde1ae23Sclaudio ipdu = pdu_getbuf(p, NULL, PDU_HEADER); 447bde1ae23Sclaudio switch (ISCSI_PDU_OPCODE(ipdu->opcode)) { 448186d2732Sclaudio case ISCSI_OP_I_NOP: 449186d2732Sclaudio case ISCSI_OP_SCSI_REQUEST: 450bde1ae23Sclaudio case ISCSI_OP_TASK_REQUEST: 451186d2732Sclaudio case ISCSI_OP_LOGIN_REQUEST: 452186d2732Sclaudio case ISCSI_OP_TEXT_REQUEST: 453186d2732Sclaudio case ISCSI_OP_DATA_OUT: 454186d2732Sclaudio case ISCSI_OP_LOGOUT_REQUEST: 455186d2732Sclaudio case ISCSI_OP_SNACK_REQUEST: 456bde1ae23Sclaudio ipdu->expstatsn = ntohl(c->expstatsn); 457bde1ae23Sclaudio break; 458bde1ae23Sclaudio } 459bde1ae23Sclaudio 460bde1ae23Sclaudio TAILQ_INSERT_TAIL(&c->pdu_w, p, entry); 461bde1ae23Sclaudio event_add(&c->wev, NULL); 462bde1ae23Sclaudio } 463bde1ae23Sclaudio 464bde1ae23Sclaudio /* connection state machine more or less as specified in the RFC */ 465bde1ae23Sclaudio struct { 466bde1ae23Sclaudio int state; 467bde1ae23Sclaudio enum c_event event; 468bde1ae23Sclaudio int (*action)(struct connection *, enum c_event); 469bde1ae23Sclaudio } fsm[] = { 470cb408c6cSclaudio { CONN_FREE, CONN_EV_CONNECT, c_do_connect }, /* T1 */ 471cb408c6cSclaudio { CONN_XPT_WAIT, CONN_EV_CONNECTED, c_do_login }, /* T4 */ 472cb408c6cSclaudio { CONN_IN_LOGIN, CONN_EV_LOGGED_IN, c_do_loggedin }, /* T5 */ 473cb408c6cSclaudio { CONN_LOGGED_IN, CONN_EV_LOGOUT, c_do_logout }, /* T9 */ 474daed3c83Sclaudio { CONN_LOGGED_IN, CONN_EV_REQ_LOGOUT, c_do_req_logout },/* T11 */ 475cb408c6cSclaudio { CONN_LOGOUT_REQ, CONN_EV_LOGOUT, c_do_logout }, /* T10 */ 476daed3c83Sclaudio { CONN_LOGOUT_REQ, CONN_EV_REQ_LOGOUT, c_do_req_logout},/* T12 */ 477daed3c83Sclaudio { CONN_LOGOUT_REQ, CONN_EV_LOGGED_OUT, c_do_loggedout },/* T18 */ 478cb408c6cSclaudio { CONN_IN_LOGOUT, CONN_EV_LOGGED_OUT, c_do_loggedout }, /* T13 */ 479daed3c83Sclaudio { CONN_IN_LOGOUT, CONN_EV_REQ_LOGOUT, c_do_req_logout },/* T14 */ 480daed3c83Sclaudio { CONN_CLEANUP_WAIT, CONN_EV_CLEANING_UP, c_do_cleanup},/* M2 */ 481daed3c83Sclaudio { CONN_CLEANUP_WAIT, CONN_EV_FREE, c_do_loggedout }, /* M1 */ 482daed3c83Sclaudio { CONN_IN_CLEANUP, CONN_EV_FREE, c_do_loggedout }, /* M4 */ 483daed3c83Sclaudio { CONN_IN_CLEANUP, CONN_EV_CLEANING_UP, c_do_cleanup}, 484daed3c83Sclaudio /* either one of T2, T7, T15, T16, T17, M3 */ 485cb408c6cSclaudio { CONN_ANYSTATE, CONN_EV_CLOSED, c_do_fail }, 486cb408c6cSclaudio { CONN_ANYSTATE, CONN_EV_FAIL, c_do_fail }, 48712bd4d7fSclaudio { CONN_ANYSTATE, CONN_EV_FREE, c_do_fail }, 488bde1ae23Sclaudio { 0, 0, NULL } 489bde1ae23Sclaudio }; 490bde1ae23Sclaudio 491bde1ae23Sclaudio void 492bde1ae23Sclaudio conn_fsm(struct connection *c, enum c_event event) 493bde1ae23Sclaudio { 494bde1ae23Sclaudio int i, ns; 495bde1ae23Sclaudio 496bde1ae23Sclaudio for (i = 0; fsm[i].action != NULL; i++) { 497bde1ae23Sclaudio if (c->state & fsm[i].state && event == fsm[i].event) { 498cb408c6cSclaudio log_debug("conn_fsm[%s]: %s ev %s", 499cb408c6cSclaudio c->session->config.SessionName, 500cb408c6cSclaudio conn_state(c->state), conn_event(event)); 501bde1ae23Sclaudio ns = fsm[i].action(c, event); 502bde1ae23Sclaudio if (ns == -1) 503bde1ae23Sclaudio /* XXX better please */ 504bde1ae23Sclaudio fatalx("conn_fsm: action failed"); 505cb408c6cSclaudio log_debug("conn_fsm[%s]: new state %s", 506cb408c6cSclaudio c->session->config.SessionName, conn_state(ns)); 507bde1ae23Sclaudio c->state = ns; 508bde1ae23Sclaudio return; 509bde1ae23Sclaudio } 510bde1ae23Sclaudio } 511454bf635Sclaudio log_warnx("conn_fsm[%s]: unhandled state transition [%s, %s]", 512454bf635Sclaudio c->session->config.SessionName, conn_state(c->state), 513454bf635Sclaudio conn_event(event)); 514bde1ae23Sclaudio fatalx("bork bork bork"); 515bde1ae23Sclaudio } 516bde1ae23Sclaudio 517bde1ae23Sclaudio int 518bde1ae23Sclaudio c_do_connect(struct connection *c, enum c_event ev) 519bde1ae23Sclaudio { 520cb408c6cSclaudio if (c->fd == -1) { 521cb408c6cSclaudio log_warnx("connect(%s), lost socket", 522cb408c6cSclaudio log_sockaddr(&c->config.TargetAddr)); 5230908bd10Sclaudio session_fsm(&c->sev, SESS_EV_CONN_FAIL, 0); 524be16f395Sclaudio return CONN_FREE; 525cb408c6cSclaudio } 5264b539f84Sclaudio if (c->config.LocalAddr.ss_len != 0) { 5274b539f84Sclaudio if (bind(c->fd, (struct sockaddr *)&c->config.LocalAddr, 5284b539f84Sclaudio c->config.LocalAddr.ss_len) == -1) { 5294b539f84Sclaudio log_warn("bind(%s)", 5304b539f84Sclaudio log_sockaddr(&c->config.LocalAddr)); 5310908bd10Sclaudio session_fsm(&c->sev, SESS_EV_CONN_FAIL, 0); 5324b539f84Sclaudio return CONN_FREE; 5334b539f84Sclaudio } 5344b539f84Sclaudio } 535bde1ae23Sclaudio if (connect(c->fd, (struct sockaddr *)&c->config.TargetAddr, 536bde1ae23Sclaudio c->config.TargetAddr.ss_len) == -1) { 537bde1ae23Sclaudio if (errno == EINPROGRESS) { 538bde1ae23Sclaudio event_add(&c->wev, NULL); 539daed3c83Sclaudio event_add(&c->ev, NULL); 540be16f395Sclaudio return CONN_XPT_WAIT; 541bde1ae23Sclaudio } else { 542bde1ae23Sclaudio log_warn("connect(%s)", 543bde1ae23Sclaudio log_sockaddr(&c->config.TargetAddr)); 5440908bd10Sclaudio session_fsm(&c->sev, SESS_EV_CONN_FAIL, 0); 545be16f395Sclaudio return CONN_FREE; 546bde1ae23Sclaudio } 547bde1ae23Sclaudio } 548daed3c83Sclaudio event_add(&c->ev, NULL); 549bde1ae23Sclaudio /* move forward */ 550be16f395Sclaudio return c_do_login(c, CONN_EV_CONNECTED); 551bde1ae23Sclaudio } 552bde1ae23Sclaudio 553bde1ae23Sclaudio int 554bde1ae23Sclaudio c_do_login(struct connection *c, enum c_event ev) 555bde1ae23Sclaudio { 556bde1ae23Sclaudio /* start a login session and hope for the best ... */ 557bde1ae23Sclaudio initiator_login(c); 558be16f395Sclaudio return CONN_IN_LOGIN; 559bde1ae23Sclaudio } 560bde1ae23Sclaudio 561bde1ae23Sclaudio int 562bde1ae23Sclaudio c_do_loggedin(struct connection *c, enum c_event ev) 563bde1ae23Sclaudio { 5647bc8b37fSclaudio iscsi_merge_conn_params(&c->active, &c->mine, &c->his); 5650908bd10Sclaudio session_fsm(&c->sev, SESS_EV_CONN_LOGGED_IN, 0); 56612bd4d7fSclaudio 567be16f395Sclaudio return CONN_LOGGED_IN; 568bde1ae23Sclaudio } 569bde1ae23Sclaudio 570bde1ae23Sclaudio int 571daed3c83Sclaudio c_do_req_logout(struct connection *c, enum c_event ev) 572daed3c83Sclaudio { 573daed3c83Sclaudio /* target requested logout. XXX implement async handler */ 574daed3c83Sclaudio 575daed3c83Sclaudio if (c->state & CONN_IN_LOGOUT) 576daed3c83Sclaudio return CONN_IN_LOGOUT; 577daed3c83Sclaudio else 578daed3c83Sclaudio return CONN_LOGOUT_REQ; 579daed3c83Sclaudio } 580daed3c83Sclaudio 581daed3c83Sclaudio int 582bde1ae23Sclaudio c_do_logout(struct connection *c, enum c_event ev) 583bde1ae23Sclaudio { 58412bd4d7fSclaudio /* logout is in progress ... */ 585be16f395Sclaudio return CONN_IN_LOGOUT; 586cb408c6cSclaudio } 587cb408c6cSclaudio 588cb408c6cSclaudio int 589cb408c6cSclaudio c_do_loggedout(struct connection *c, enum c_event ev) 590cb408c6cSclaudio { 591daed3c83Sclaudio /* 592daed3c83Sclaudio * Called by the session fsm before calling conn_free. 593daed3c83Sclaudio * Doing this so the state transition is logged. 594daed3c83Sclaudio */ 595be16f395Sclaudio return CONN_FREE; 596bde1ae23Sclaudio } 597bde1ae23Sclaudio 598cb408c6cSclaudio int 599cb408c6cSclaudio c_do_fail(struct connection *c, enum c_event ev) 600cb408c6cSclaudio { 601daed3c83Sclaudio log_debug("c_do_fail"); 602daed3c83Sclaudio 603cb408c6cSclaudio /* cleanup events so that the connection does not retrigger */ 604cb408c6cSclaudio event_del(&c->ev); 605cb408c6cSclaudio event_del(&c->wev); 606cb408c6cSclaudio close(c->fd); 607daed3c83Sclaudio c->fd = -1; /* make sure this fd is not closed again */ 608cb408c6cSclaudio 609daed3c83Sclaudio /* all pending task have failed so clean them up */ 610daed3c83Sclaudio taskq_cleanup(&c->tasks); 611daed3c83Sclaudio 612daed3c83Sclaudio /* session will take care of cleaning up the mess */ 6130908bd10Sclaudio session_fsm(&c->sev, SESS_EV_CONN_FAIL, 0); 614cb408c6cSclaudio 61512bd4d7fSclaudio if (ev == CONN_EV_FREE || c->state & CONN_NEVER_LOGGED_IN) 616be16f395Sclaudio return CONN_FREE; 617be16f395Sclaudio return CONN_CLEANUP_WAIT; 618cb408c6cSclaudio } 619cb408c6cSclaudio 620daed3c83Sclaudio int 621daed3c83Sclaudio c_do_cleanup(struct connection *c, enum c_event ev) 622daed3c83Sclaudio { 623daed3c83Sclaudio /* nothing to do here just adjust state */ 624daed3c83Sclaudio return CONN_IN_CLEANUP; 625daed3c83Sclaudio } 626daed3c83Sclaudio 627bde1ae23Sclaudio const char * 628bde1ae23Sclaudio conn_state(int s) 629bde1ae23Sclaudio { 630bde1ae23Sclaudio static char buf[15]; 631bde1ae23Sclaudio 632bde1ae23Sclaudio switch (s) { 633bde1ae23Sclaudio case CONN_FREE: 634bde1ae23Sclaudio return "FREE"; 635bde1ae23Sclaudio case CONN_XPT_WAIT: 636bde1ae23Sclaudio return "XPT_WAIT"; 637bde1ae23Sclaudio case CONN_XPT_UP: 638bde1ae23Sclaudio return "XPT_UP"; 639bde1ae23Sclaudio case CONN_IN_LOGIN: 640bde1ae23Sclaudio return "IN_LOGIN"; 641bde1ae23Sclaudio case CONN_LOGGED_IN: 642bde1ae23Sclaudio return "LOGGED_IN"; 643bde1ae23Sclaudio case CONN_IN_LOGOUT: 644bde1ae23Sclaudio return "IN_LOGOUT"; 645bde1ae23Sclaudio case CONN_LOGOUT_REQ: 646bde1ae23Sclaudio return "LOGOUT_REQ"; 647bde1ae23Sclaudio case CONN_CLEANUP_WAIT: 648bde1ae23Sclaudio return "CLEANUP_WAIT"; 649bde1ae23Sclaudio case CONN_IN_CLEANUP: 650bde1ae23Sclaudio return "IN_CLEANUP"; 651bde1ae23Sclaudio default: 652bde1ae23Sclaudio snprintf(buf, sizeof(buf), "UKNWN %x", s); 653bde1ae23Sclaudio return buf; 654bde1ae23Sclaudio } 655bde1ae23Sclaudio /* NOTREACHED */ 656bde1ae23Sclaudio } 657bde1ae23Sclaudio 658bde1ae23Sclaudio const char * 659bde1ae23Sclaudio conn_event(enum c_event e) 660bde1ae23Sclaudio { 661bde1ae23Sclaudio static char buf[15]; 662bde1ae23Sclaudio 663bde1ae23Sclaudio switch (e) { 664bde1ae23Sclaudio case CONN_EV_FAIL: 665bde1ae23Sclaudio return "fail"; 666bde1ae23Sclaudio case CONN_EV_CONNECT: 667bde1ae23Sclaudio return "connect"; 668bde1ae23Sclaudio case CONN_EV_CONNECTED: 669bde1ae23Sclaudio return "connected"; 670bde1ae23Sclaudio case CONN_EV_LOGGED_IN: 671bde1ae23Sclaudio return "logged in"; 672daed3c83Sclaudio case CONN_EV_REQ_LOGOUT: 673daed3c83Sclaudio return "logout requested"; 674cb408c6cSclaudio case CONN_EV_LOGOUT: 675cb408c6cSclaudio return "logout"; 676cb408c6cSclaudio case CONN_EV_LOGGED_OUT: 677cb408c6cSclaudio return "logged out"; 678daed3c83Sclaudio case CONN_EV_CLEANING_UP: 679daed3c83Sclaudio return "cleaning up"; 680cb408c6cSclaudio case CONN_EV_CLOSED: 681cb408c6cSclaudio return "closed"; 68212bd4d7fSclaudio case CONN_EV_FREE: 68312bd4d7fSclaudio return "forced free"; 684bde1ae23Sclaudio } 685bde1ae23Sclaudio 686bde1ae23Sclaudio snprintf(buf, sizeof(buf), "UKNWN %d", e); 687bde1ae23Sclaudio return buf; 688bde1ae23Sclaudio } 689