1*2224f3a1Sclaudio /* $OpenBSD: initiator.c,v 1.21 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 <scsi/iscsi.h> 25bde1ae23Sclaudio 26bde1ae23Sclaudio #include <event.h> 27bde1ae23Sclaudio #include <stdio.h> 28bde1ae23Sclaudio #include <stdlib.h> 29bde1ae23Sclaudio #include <string.h> 30bde1ae23Sclaudio #include <unistd.h> 31ea0867c0Sderaadt #include <limits.h> 32bde1ae23Sclaudio 33bde1ae23Sclaudio #include "iscsid.h" 34bde1ae23Sclaudio #include "log.h" 35bde1ae23Sclaudio 367b833938Sclaudio static struct initiator *initiator; 37bde1ae23Sclaudio 384125a3c4Sclaudio struct task_login { 394125a3c4Sclaudio struct task task; 404125a3c4Sclaudio struct connection *c; 414125a3c4Sclaudio u_int16_t tsih; 424125a3c4Sclaudio u_int8_t stage; 434125a3c4Sclaudio }; 444125a3c4Sclaudio 454125a3c4Sclaudio struct task_logout { 464125a3c4Sclaudio struct task task; 474125a3c4Sclaudio struct connection *c; 484125a3c4Sclaudio u_int8_t reason; 494125a3c4Sclaudio }; 504125a3c4Sclaudio 51*2224f3a1Sclaudio int conn_is_leading(struct connection *); 524125a3c4Sclaudio struct kvp *initiator_login_kvp(struct connection *, u_int8_t); 534125a3c4Sclaudio struct pdu *initiator_login_build(struct connection *, 544125a3c4Sclaudio struct task_login *); 554125a3c4Sclaudio struct pdu *initiator_text_build(struct task *, struct session *, 564125a3c4Sclaudio struct kvp *); 574125a3c4Sclaudio 584125a3c4Sclaudio void initiator_login_cb(struct connection *, void *, struct pdu *); 594125a3c4Sclaudio void initiator_discovery_cb(struct connection *, void *, struct pdu *); 604125a3c4Sclaudio void initiator_logout_cb(struct connection *, void *, struct pdu *); 614125a3c4Sclaudio 624125a3c4Sclaudio struct session_params initiator_sess_defaults; 634125a3c4Sclaudio struct connection_params initiator_conn_defaults; 64bde1ae23Sclaudio 657b833938Sclaudio void 66bde1ae23Sclaudio initiator_init(void) 67bde1ae23Sclaudio { 68bde1ae23Sclaudio if (!(initiator = calloc(1, sizeof(*initiator)))) 69bde1ae23Sclaudio fatal("initiator_init"); 70bde1ae23Sclaudio 71bde1ae23Sclaudio initiator->config.isid_base = 72bde1ae23Sclaudio arc4random_uniform(0xffffff) | ISCSI_ISID_RAND; 73bde1ae23Sclaudio initiator->config.isid_qual = arc4random_uniform(0xffff); 74bde1ae23Sclaudio TAILQ_INIT(&initiator->sessions); 754125a3c4Sclaudio 764125a3c4Sclaudio /* initialize initiator defaults */ 774125a3c4Sclaudio initiator_sess_defaults = iscsi_sess_defaults; 784125a3c4Sclaudio initiator_conn_defaults = iscsi_conn_defaults; 7981d087b1Sclaudio initiator_sess_defaults.MaxConnections = ISCSID_DEF_CONNS; 804125a3c4Sclaudio initiator_conn_defaults.MaxRecvDataSegmentLength = 65536; 81bde1ae23Sclaudio } 82bde1ae23Sclaudio 83bde1ae23Sclaudio void 847b833938Sclaudio initiator_cleanup(void) 85bde1ae23Sclaudio { 86bde1ae23Sclaudio struct session *s; 87bde1ae23Sclaudio 887b833938Sclaudio while ((s = TAILQ_FIRST(&initiator->sessions)) != NULL) { 897b833938Sclaudio TAILQ_REMOVE(&initiator->sessions, s, entry); 90cb408c6cSclaudio session_cleanup(s); 91bde1ae23Sclaudio } 92bde1ae23Sclaudio free(initiator); 93bde1ae23Sclaudio } 94bde1ae23Sclaudio 9512bd4d7fSclaudio void 967b833938Sclaudio initiator_set_config(struct initiator_config *ic) 977b833938Sclaudio { 987b833938Sclaudio initiator->config = *ic; 997b833938Sclaudio } 1007b833938Sclaudio 1017b833938Sclaudio struct initiator_config * 1027b833938Sclaudio initiator_get_config(void) 1037b833938Sclaudio { 1047b833938Sclaudio return &initiator->config; 1057b833938Sclaudio } 1067b833938Sclaudio 1077b833938Sclaudio void 1087b833938Sclaudio initiator_shutdown(void) 10912bd4d7fSclaudio { 11012bd4d7fSclaudio struct session *s; 11112bd4d7fSclaudio 11212bd4d7fSclaudio log_debug("initiator_shutdown: going down"); 11312bd4d7fSclaudio 11412bd4d7fSclaudio TAILQ_FOREACH(s, &initiator->sessions, entry) 11512bd4d7fSclaudio session_shutdown(s); 11612bd4d7fSclaudio } 11712bd4d7fSclaudio 11812bd4d7fSclaudio int 1197b833938Sclaudio initiator_isdown(void) 12012bd4d7fSclaudio { 12112bd4d7fSclaudio struct session *s; 12212bd4d7fSclaudio int inprogres = 0; 12312bd4d7fSclaudio 12412bd4d7fSclaudio TAILQ_FOREACH(s, &initiator->sessions, entry) { 12512bd4d7fSclaudio if ((s->state & SESS_RUNNING) && !(s->state & SESS_FREE)) 12612bd4d7fSclaudio inprogres = 1; 12712bd4d7fSclaudio } 12812bd4d7fSclaudio return !inprogres; 12912bd4d7fSclaudio } 13012bd4d7fSclaudio 131bde1ae23Sclaudio struct session * 1327b833938Sclaudio initiator_new_session(u_int8_t st) 1337b833938Sclaudio { 1347b833938Sclaudio struct session *s; 1357b833938Sclaudio 1367b833938Sclaudio if (!(s = calloc(1, sizeof(*s)))) 1377b833938Sclaudio return NULL; 1387b833938Sclaudio 1397b833938Sclaudio /* use the same qualifier unless there is a conflict */ 1407b833938Sclaudio s->isid_base = initiator->config.isid_base; 1417b833938Sclaudio s->isid_qual = initiator->config.isid_qual; 1427b833938Sclaudio s->cmdseqnum = arc4random(); 1437b833938Sclaudio s->itt = arc4random(); 1447b833938Sclaudio s->state = SESS_INIT; 1457b833938Sclaudio 1467b833938Sclaudio s->sev.sess = s; 1477b833938Sclaudio evtimer_set(&s->sev.ev, session_fsm_callback, &s->sev); 1487b833938Sclaudio 1497b833938Sclaudio if (st == SESSION_TYPE_DISCOVERY) 1507b833938Sclaudio s->target = 0; 1517b833938Sclaudio else 1527b833938Sclaudio s->target = initiator->target++; 1537b833938Sclaudio 1547b833938Sclaudio TAILQ_INIT(&s->connections); 1557b833938Sclaudio TAILQ_INIT(&s->tasks); 1567b833938Sclaudio 1577b833938Sclaudio TAILQ_INSERT_HEAD(&initiator->sessions, s, entry); 1587b833938Sclaudio 1597b833938Sclaudio return s; 1607b833938Sclaudio } 1617b833938Sclaudio 1627b833938Sclaudio struct session * 1637b833938Sclaudio initiator_find_session(char *name) 1647b833938Sclaudio { 1657b833938Sclaudio struct session *s; 1667b833938Sclaudio 1677b833938Sclaudio TAILQ_FOREACH(s, &initiator->sessions, entry) { 1687b833938Sclaudio if (strcmp(s->config.SessionName, name) == 0) 1697b833938Sclaudio return s; 1707b833938Sclaudio } 1717b833938Sclaudio return NULL; 1727b833938Sclaudio } 1737b833938Sclaudio 1747b833938Sclaudio struct session * 175bde1ae23Sclaudio initiator_t2s(u_int target) 176bde1ae23Sclaudio { 177bde1ae23Sclaudio struct session *s; 178bde1ae23Sclaudio 179bde1ae23Sclaudio TAILQ_FOREACH(s, &initiator->sessions, entry) { 180bde1ae23Sclaudio if (s->target == target) 181bde1ae23Sclaudio return s; 182bde1ae23Sclaudio } 183bde1ae23Sclaudio return NULL; 184bde1ae23Sclaudio } 185bde1ae23Sclaudio 1867b833938Sclaudio struct session_head * 1877b833938Sclaudio initiator_get_sessions(void) 1887b833938Sclaudio { 1897b833938Sclaudio return &initiator->sessions; 1907b833938Sclaudio } 1917b833938Sclaudio 192bde1ae23Sclaudio void 193bde1ae23Sclaudio initiator_login(struct connection *c) 194bde1ae23Sclaudio { 195bde1ae23Sclaudio struct task_login *tl; 196bde1ae23Sclaudio struct pdu *p; 197bde1ae23Sclaudio 198bde1ae23Sclaudio if (!(tl = calloc(1, sizeof(*tl)))) { 199bde1ae23Sclaudio log_warn("initiator_login"); 200bde1ae23Sclaudio conn_fail(c); 201bde1ae23Sclaudio return; 202bde1ae23Sclaudio } 203bde1ae23Sclaudio tl->c = c; 204bde1ae23Sclaudio tl->stage = ISCSI_LOGIN_STG_SECNEG; 205bde1ae23Sclaudio 2064125a3c4Sclaudio if (!(p = initiator_login_build(c, tl))) { 207cb408c6cSclaudio log_warn("initiator_login_build failed"); 208bde1ae23Sclaudio free(tl); 209bde1ae23Sclaudio conn_fail(c); 210bde1ae23Sclaudio return; 211bde1ae23Sclaudio } 212bde1ae23Sclaudio 213cb408c6cSclaudio task_init(&tl->task, c->session, 1, tl, initiator_login_cb, NULL); 214bde1ae23Sclaudio task_pdu_add(&tl->task, p); 215cb408c6cSclaudio conn_task_issue(c, &tl->task); 216bde1ae23Sclaudio } 217bde1ae23Sclaudio 218bde1ae23Sclaudio void 219bde1ae23Sclaudio initiator_discovery(struct session *s) 220bde1ae23Sclaudio { 221bde1ae23Sclaudio struct task *t; 222bde1ae23Sclaudio struct pdu *p; 223bde1ae23Sclaudio struct kvp kvp[] = { 224bde1ae23Sclaudio { "SendTargets", "All" }, 225bde1ae23Sclaudio { NULL, NULL } 226bde1ae23Sclaudio }; 227bde1ae23Sclaudio 228bde1ae23Sclaudio if (!(t = calloc(1, sizeof(*t)))) { 229bde1ae23Sclaudio log_warn("initiator_discovery"); 2304125a3c4Sclaudio /* XXX sess_fail(c); */ 231bde1ae23Sclaudio return; 232bde1ae23Sclaudio } 233bde1ae23Sclaudio 234bde1ae23Sclaudio if (!(p = initiator_text_build(t, s, kvp))) { 235bde1ae23Sclaudio log_warnx("initiator_text_build failed"); 23617fa8594Sclaudio free(t); 2374125a3c4Sclaudio /* XXX sess_fail(c); */ 238bde1ae23Sclaudio return; 239bde1ae23Sclaudio } 240bde1ae23Sclaudio 241cb408c6cSclaudio task_init(t, s, 0, t, initiator_discovery_cb, NULL); 242bde1ae23Sclaudio task_pdu_add(t, p); 243bde1ae23Sclaudio session_task_issue(s, t); 244bde1ae23Sclaudio } 245bde1ae23Sclaudio 246cb408c6cSclaudio void 2474125a3c4Sclaudio initiator_logout(struct session *s, struct connection *c, u_int8_t reason) 2484125a3c4Sclaudio { 2494125a3c4Sclaudio struct task_logout *tl; 2504125a3c4Sclaudio struct pdu *p; 2514125a3c4Sclaudio struct iscsi_pdu_logout_request *loreq; 2524125a3c4Sclaudio 2534125a3c4Sclaudio if (!(tl = calloc(1, sizeof(*tl)))) { 2544125a3c4Sclaudio log_warn("initiator_logout"); 2554125a3c4Sclaudio /* XXX sess_fail */ 2564125a3c4Sclaudio return; 2574125a3c4Sclaudio } 2584125a3c4Sclaudio tl->c = c; 2594125a3c4Sclaudio tl->reason = reason; 2604125a3c4Sclaudio 2614125a3c4Sclaudio if (!(p = pdu_new())) { 2624125a3c4Sclaudio log_warn("initiator_logout"); 2634125a3c4Sclaudio /* XXX sess_fail */ 2644125a3c4Sclaudio free(tl); 2654125a3c4Sclaudio return; 2664125a3c4Sclaudio } 2674125a3c4Sclaudio if (!(loreq = pdu_gethdr(p))) { 2684125a3c4Sclaudio log_warn("initiator_logout"); 2694125a3c4Sclaudio /* XXX sess_fail */ 2704125a3c4Sclaudio pdu_free(p); 2714125a3c4Sclaudio free(tl); 2724125a3c4Sclaudio return; 2734125a3c4Sclaudio } 2744125a3c4Sclaudio 2754125a3c4Sclaudio loreq->opcode = ISCSI_OP_LOGOUT_REQUEST; 2764125a3c4Sclaudio loreq->flags = ISCSI_LOGOUT_F | reason; 2774125a3c4Sclaudio if (reason != ISCSI_LOGOUT_CLOSE_SESS) 2784125a3c4Sclaudio loreq->cid = c->cid; 2794125a3c4Sclaudio 2804125a3c4Sclaudio task_init(&tl->task, s, 0, tl, initiator_logout_cb, NULL); 2814125a3c4Sclaudio task_pdu_add(&tl->task, p); 2824125a3c4Sclaudio if (c && (c->state & CONN_RUNNING)) 2834125a3c4Sclaudio conn_task_issue(c, &tl->task); 2844125a3c4Sclaudio else 2854125a3c4Sclaudio session_logout_issue(s, &tl->task); 2864125a3c4Sclaudio } 2874125a3c4Sclaudio 2884125a3c4Sclaudio void 2894125a3c4Sclaudio initiator_nop_in_imm(struct connection *c, struct pdu *p) 2904125a3c4Sclaudio { 2914125a3c4Sclaudio struct iscsi_pdu_nop_in *nopin; 2924125a3c4Sclaudio struct task *t; 2934125a3c4Sclaudio 2944125a3c4Sclaudio /* fixup NOP-IN to make it a NOP-OUT */ 2954125a3c4Sclaudio nopin = pdu_getbuf(p, NULL, PDU_HEADER); 2964125a3c4Sclaudio nopin->maxcmdsn = 0; 2974125a3c4Sclaudio nopin->opcode = ISCSI_OP_I_NOP | ISCSI_OP_F_IMMEDIATE; 2984125a3c4Sclaudio 2994125a3c4Sclaudio /* and schedule an immediate task */ 3004125a3c4Sclaudio if (!(t = calloc(1, sizeof(*t)))) { 3014125a3c4Sclaudio log_warn("initiator_nop_in_imm"); 3024125a3c4Sclaudio pdu_free(p); 3034125a3c4Sclaudio return; 3044125a3c4Sclaudio } 3054125a3c4Sclaudio 3064125a3c4Sclaudio task_init(t, c->session, 1, NULL, NULL, NULL); 3074125a3c4Sclaudio t->itt = 0xffffffff; /* change ITT because it is just a ping reply */ 3084125a3c4Sclaudio task_pdu_add(t, p); 3094125a3c4Sclaudio conn_task_issue(c, t); 3104125a3c4Sclaudio } 3114125a3c4Sclaudio 312*2224f3a1Sclaudio int 313*2224f3a1Sclaudio conn_is_leading(struct connection *c) 314*2224f3a1Sclaudio { 315*2224f3a1Sclaudio return c == TAILQ_FIRST(&c->session->connections); 316*2224f3a1Sclaudio } 317*2224f3a1Sclaudio 318*2224f3a1Sclaudio #define MINE_NOT_DEFAULT(c, k) ((c)->mine.k != iscsi_conn_defaults.k) 319*2224f3a1Sclaudio 3204125a3c4Sclaudio struct kvp * 3214125a3c4Sclaudio initiator_login_kvp(struct connection *c, u_int8_t stage) 3224125a3c4Sclaudio { 323*2224f3a1Sclaudio struct kvp *kvp = NULL; 324*2224f3a1Sclaudio size_t i = 0, len; 325*2224f3a1Sclaudio const char *discovery[] = {"SessionType", "InitiatorName", 326*2224f3a1Sclaudio "AuthMethod", NULL}; 327*2224f3a1Sclaudio const char *leading_only[] = {"MaxConnections", "InitialR2T", 328*2224f3a1Sclaudio "ImmediateData", "MaxBurstLength", "FirstBurstLength", 329*2224f3a1Sclaudio "DefaultTime2Wait", "DefaultTime2Retain", "MaxOutstandingR2T", 330*2224f3a1Sclaudio "DataPDUInOrder", "DataSequenceInOrder", "ErrorRecoveryLevel", 331*2224f3a1Sclaudio NULL}; 332*2224f3a1Sclaudio const char *opneg_always[] = {"HeaderDigest", "DataDigest", NULL}; 333*2224f3a1Sclaudio const char *secneg[] = {"SessionType", "InitiatorName", "TargetName", 334*2224f3a1Sclaudio "AuthMethod", NULL}; 335*2224f3a1Sclaudio const char **p, **q; 3364125a3c4Sclaudio 3374125a3c4Sclaudio switch (stage) { 3384125a3c4Sclaudio case ISCSI_LOGIN_STG_SECNEG: 3394125a3c4Sclaudio if (c->session->config.SessionType == SESSION_TYPE_DISCOVERY) { 340*2224f3a1Sclaudio len = sizeof(discovery) / sizeof(*discovery); 341*2224f3a1Sclaudio q = discovery; 3424125a3c4Sclaudio } else { 343*2224f3a1Sclaudio len = sizeof(secneg) / sizeof(*secneg); 344*2224f3a1Sclaudio q = secneg; 3454125a3c4Sclaudio } 346*2224f3a1Sclaudio if (!(kvp = calloc(len + 1, sizeof(*kvp)))) 347*2224f3a1Sclaudio return NULL; 348*2224f3a1Sclaudio for (p = q; *p != NULL; i++, p++) 349*2224f3a1Sclaudio if (kvp_set_from_mine(&kvp[i], *p, c)) 350*2224f3a1Sclaudio goto fail; 3514125a3c4Sclaudio break; 3524125a3c4Sclaudio case ISCSI_LOGIN_STG_OPNEG: 353*2224f3a1Sclaudio len = sizeof(opneg_always) / sizeof(*opneg_always); 354*2224f3a1Sclaudio if (conn_is_leading(c)) 355*2224f3a1Sclaudio len += sizeof(leading_only) / sizeof(*leading_only); 356*2224f3a1Sclaudio if (MINE_NOT_DEFAULT(c, MaxRecvDataSegmentLength)) 357*2224f3a1Sclaudio len++; 358*2224f3a1Sclaudio if (!(kvp = calloc(len + 1, sizeof(*kvp)))) 3594125a3c4Sclaudio return NULL; 360*2224f3a1Sclaudio for (p = opneg_always; *p != NULL; i++, p++) 361*2224f3a1Sclaudio if (kvp_set_from_mine(&kvp[i], *p, c)) 362*2224f3a1Sclaudio goto fail; 363*2224f3a1Sclaudio if (conn_is_leading(c)) 364*2224f3a1Sclaudio for (p = leading_only; *p != NULL; i++, p++) 365*2224f3a1Sclaudio if (kvp_set_from_mine(&kvp[i], *p, c)) 366*2224f3a1Sclaudio goto fail; 367*2224f3a1Sclaudio if (MINE_NOT_DEFAULT(c, MaxRecvDataSegmentLength) && 368*2224f3a1Sclaudio kvp_set_from_mine(&kvp[i], "MaxRecvDataSegmentLength", c)) 369*2224f3a1Sclaudio goto fail; 3704125a3c4Sclaudio break; 3714125a3c4Sclaudio default: 3724125a3c4Sclaudio log_warnx("initiator_login_kvp: exit stage left"); 3734125a3c4Sclaudio return NULL; 3744125a3c4Sclaudio } 3754125a3c4Sclaudio return kvp; 376*2224f3a1Sclaudio fail: 377*2224f3a1Sclaudio kvp_free(kvp); 378*2224f3a1Sclaudio return NULL; 3794125a3c4Sclaudio } 3804125a3c4Sclaudio 381*2224f3a1Sclaudio #undef MINE_NOT_DEFAULT 3824125a3c4Sclaudio struct pdu * 3834125a3c4Sclaudio initiator_login_build(struct connection *c, struct task_login *tl) 3844125a3c4Sclaudio { 3854125a3c4Sclaudio struct pdu *p; 3864125a3c4Sclaudio struct kvp *kvp; 3874125a3c4Sclaudio struct iscsi_pdu_login_request *lreq; 3884125a3c4Sclaudio int n; 3894125a3c4Sclaudio 3904125a3c4Sclaudio if (!(p = pdu_new())) 3914125a3c4Sclaudio return NULL; 3924125a3c4Sclaudio if (!(lreq = pdu_gethdr(p))) { 3934125a3c4Sclaudio pdu_free(p); 3944125a3c4Sclaudio return NULL; 3954125a3c4Sclaudio } 3964125a3c4Sclaudio 3974125a3c4Sclaudio lreq->opcode = ISCSI_OP_LOGIN_REQUEST | ISCSI_OP_F_IMMEDIATE; 3984125a3c4Sclaudio if (tl->stage == ISCSI_LOGIN_STG_SECNEG) 3994125a3c4Sclaudio lreq->flags = ISCSI_LOGIN_F_T | 4004125a3c4Sclaudio ISCSI_LOGIN_F_CSG(ISCSI_LOGIN_STG_SECNEG) | 4014125a3c4Sclaudio ISCSI_LOGIN_F_NSG(ISCSI_LOGIN_STG_OPNEG); 4024125a3c4Sclaudio else if (tl->stage == ISCSI_LOGIN_STG_OPNEG) 4034125a3c4Sclaudio lreq->flags = ISCSI_LOGIN_F_T | 4044125a3c4Sclaudio ISCSI_LOGIN_F_CSG(ISCSI_LOGIN_STG_OPNEG) | 4054125a3c4Sclaudio ISCSI_LOGIN_F_NSG(ISCSI_LOGIN_STG_FULL); 4064125a3c4Sclaudio 4074125a3c4Sclaudio lreq->isid_base = htonl(tl->c->session->isid_base); 4084125a3c4Sclaudio lreq->isid_qual = htons(tl->c->session->isid_qual); 4094125a3c4Sclaudio lreq->tsih = tl->tsih; 4104125a3c4Sclaudio lreq->cid = htons(tl->c->cid); 4114125a3c4Sclaudio lreq->expstatsn = htonl(tl->c->expstatsn); 4124125a3c4Sclaudio 4134125a3c4Sclaudio if (!(kvp = initiator_login_kvp(c, tl->stage))) { 4144125a3c4Sclaudio log_warn("initiator_login_kvp failed"); 4154125a3c4Sclaudio return NULL; 4164125a3c4Sclaudio } 4174125a3c4Sclaudio if ((n = text_to_pdu(kvp, p)) == -1) { 418dda190faSclaudio kvp_free(kvp); 4194125a3c4Sclaudio return NULL; 4204125a3c4Sclaudio } 421dda190faSclaudio kvp_free(kvp); 4224125a3c4Sclaudio 4234125a3c4Sclaudio if (n > 8192) { 4244125a3c4Sclaudio log_warn("initiator_login_build: help, I'm too verbose"); 4254125a3c4Sclaudio pdu_free(p); 4264125a3c4Sclaudio return NULL; 4274125a3c4Sclaudio } 4284125a3c4Sclaudio n = htonl(n); 4294125a3c4Sclaudio /* copy 32bit value over ahslen and datalen */ 430a86db512Sclaudio memcpy(&lreq->ahslen, &n, sizeof(n)); 4314125a3c4Sclaudio 4324125a3c4Sclaudio return p; 4334125a3c4Sclaudio } 4344125a3c4Sclaudio 4354125a3c4Sclaudio struct pdu * 4364125a3c4Sclaudio initiator_text_build(struct task *t, struct session *s, struct kvp *kvp) 4374125a3c4Sclaudio { 4384125a3c4Sclaudio struct pdu *p; 4394125a3c4Sclaudio struct iscsi_pdu_text_request *lreq; 4404125a3c4Sclaudio int n; 4414125a3c4Sclaudio 4424125a3c4Sclaudio if (!(p = pdu_new())) 4434125a3c4Sclaudio return NULL; 4444125a3c4Sclaudio if (!(lreq = pdu_gethdr(p))) 4454125a3c4Sclaudio return NULL; 4464125a3c4Sclaudio 4474125a3c4Sclaudio lreq->opcode = ISCSI_OP_TEXT_REQUEST; 4484125a3c4Sclaudio lreq->flags = ISCSI_TEXT_F_F; 4494125a3c4Sclaudio lreq->ttt = 0xffffffff; 4504125a3c4Sclaudio 4514125a3c4Sclaudio if ((n = text_to_pdu(kvp, p)) == -1) 4524125a3c4Sclaudio return NULL; 4534125a3c4Sclaudio n = htonl(n); 454a86db512Sclaudio memcpy(&lreq->ahslen, &n, sizeof(n)); 4554125a3c4Sclaudio 4564125a3c4Sclaudio return p; 4574125a3c4Sclaudio } 4584125a3c4Sclaudio 4594125a3c4Sclaudio void 4604125a3c4Sclaudio initiator_login_cb(struct connection *c, void *arg, struct pdu *p) 4614125a3c4Sclaudio { 4624125a3c4Sclaudio struct task_login *tl = arg; 4634125a3c4Sclaudio struct iscsi_pdu_login_response *lresp; 4644125a3c4Sclaudio u_char *buf = NULL; 4654125a3c4Sclaudio struct kvp *kvp; 4664125a3c4Sclaudio size_t n, size; 4674125a3c4Sclaudio 4684125a3c4Sclaudio lresp = pdu_getbuf(p, NULL, PDU_HEADER); 4694125a3c4Sclaudio 4704125a3c4Sclaudio if (ISCSI_PDU_OPCODE(lresp->opcode) != ISCSI_OP_LOGIN_RESPONSE) { 4714125a3c4Sclaudio log_warnx("Unexpected login response type %x", 4724125a3c4Sclaudio ISCSI_PDU_OPCODE(lresp->opcode)); 4734125a3c4Sclaudio conn_fail(c); 4744125a3c4Sclaudio goto done; 4754125a3c4Sclaudio } 4764125a3c4Sclaudio 4774125a3c4Sclaudio if (lresp->flags & ISCSI_LOGIN_F_C) { 4784125a3c4Sclaudio log_warnx("Incomplete login responses are unsupported"); 4794125a3c4Sclaudio conn_fail(c); 4804125a3c4Sclaudio goto done; 4814125a3c4Sclaudio } 4824125a3c4Sclaudio 4834125a3c4Sclaudio size = lresp->datalen[0] << 16 | lresp->datalen[1] << 8 | 4844125a3c4Sclaudio lresp->datalen[2]; 4854125a3c4Sclaudio buf = pdu_getbuf(p, &n, PDU_DATA); 4864125a3c4Sclaudio if (size > n) { 4874125a3c4Sclaudio log_warnx("Bad login response"); 4884125a3c4Sclaudio conn_fail(c); 4894125a3c4Sclaudio goto done; 4904125a3c4Sclaudio } 4914125a3c4Sclaudio 4924125a3c4Sclaudio if (buf) { 4934125a3c4Sclaudio kvp = pdu_to_text(buf, size); 4944125a3c4Sclaudio if (kvp == NULL) { 4954125a3c4Sclaudio conn_fail(c); 4964125a3c4Sclaudio goto done; 4974125a3c4Sclaudio } 4984125a3c4Sclaudio 4994125a3c4Sclaudio if (conn_parse_kvp(c, kvp) == -1) { 500dda190faSclaudio kvp_free(kvp); 5014125a3c4Sclaudio conn_fail(c); 5024125a3c4Sclaudio goto done; 5034125a3c4Sclaudio } 504dda190faSclaudio kvp_free(kvp); 5054125a3c4Sclaudio } 5064125a3c4Sclaudio 5074125a3c4Sclaudio /* advance FSM if possible */ 5084125a3c4Sclaudio if (lresp->flags & ISCSI_LOGIN_F_T) 5094125a3c4Sclaudio tl->stage = ISCSI_LOGIN_F_NSG(lresp->flags); 5104125a3c4Sclaudio 5114125a3c4Sclaudio switch (tl->stage) { 5124125a3c4Sclaudio case ISCSI_LOGIN_STG_SECNEG: 5134125a3c4Sclaudio case ISCSI_LOGIN_STG_OPNEG: 5144125a3c4Sclaudio /* free no longer used pdu */ 5154125a3c4Sclaudio pdu_free(p); 5164125a3c4Sclaudio p = initiator_login_build(c, tl); 5174125a3c4Sclaudio if (p == NULL) { 5184125a3c4Sclaudio conn_fail(c); 5194125a3c4Sclaudio goto done; 5204125a3c4Sclaudio } 5214125a3c4Sclaudio break; 5224125a3c4Sclaudio case ISCSI_LOGIN_STG_FULL: 5234125a3c4Sclaudio conn_fsm(c, CONN_EV_LOGGED_IN); 52464d8b5c8Sclaudio conn_task_cleanup(c, &tl->task); 52564d8b5c8Sclaudio free(tl); 5264125a3c4Sclaudio goto done; 5274125a3c4Sclaudio default: 5284125a3c4Sclaudio log_warnx("initiator_login_cb: exit stage left"); 5294125a3c4Sclaudio conn_fail(c); 5304125a3c4Sclaudio goto done; 5314125a3c4Sclaudio } 5324125a3c4Sclaudio conn_task_cleanup(c, &tl->task); 5334125a3c4Sclaudio /* add new pdu and re-issue the task */ 5344125a3c4Sclaudio task_pdu_add(&tl->task, p); 5354125a3c4Sclaudio conn_task_issue(c, &tl->task); 5364125a3c4Sclaudio return; 5374125a3c4Sclaudio done: 5384125a3c4Sclaudio if (p) 5394125a3c4Sclaudio pdu_free(p); 5404125a3c4Sclaudio } 5414125a3c4Sclaudio 5424125a3c4Sclaudio void 543cb408c6cSclaudio initiator_discovery_cb(struct connection *c, void *arg, struct pdu *p) 544cb408c6cSclaudio { 545cb408c6cSclaudio struct task *t = arg; 546cb408c6cSclaudio struct iscsi_pdu_text_response *lresp; 547cb408c6cSclaudio u_char *buf = NULL; 548cb408c6cSclaudio struct kvp *kvp, *k; 549cb408c6cSclaudio size_t n, size; 550cb408c6cSclaudio 551cb408c6cSclaudio lresp = pdu_getbuf(p, NULL, PDU_HEADER); 552cb408c6cSclaudio switch (ISCSI_PDU_OPCODE(lresp->opcode)) { 553cb408c6cSclaudio case ISCSI_OP_TEXT_RESPONSE: 554cb408c6cSclaudio size = lresp->datalen[0] << 16 | lresp->datalen[1] << 8 | 555cb408c6cSclaudio lresp->datalen[2]; 556cb408c6cSclaudio if (size == 0) { 557cb408c6cSclaudio /* empty response */ 55812bd4d7fSclaudio session_shutdown(c->session); 559cb408c6cSclaudio break; 560cb408c6cSclaudio } 561cb408c6cSclaudio buf = pdu_getbuf(p, &n, PDU_DATA); 562cb408c6cSclaudio if (size > n || buf == NULL) 563cb408c6cSclaudio goto fail; 564cb408c6cSclaudio kvp = pdu_to_text(buf, size); 565cb408c6cSclaudio if (kvp == NULL) 566cb408c6cSclaudio goto fail; 567cb408c6cSclaudio log_debug("ISCSI_OP_TEXT_RESPONSE"); 568cb408c6cSclaudio for (k = kvp; k->key; k++) { 569cb408c6cSclaudio log_debug("%s\t=>\t%s", k->key, k->value); 570cb408c6cSclaudio } 571dda190faSclaudio kvp_free(kvp); 57212bd4d7fSclaudio session_shutdown(c->session); 573cb408c6cSclaudio break; 574cb408c6cSclaudio default: 575cb408c6cSclaudio log_debug("initiator_discovery_cb: unexpected message type %x", 576cb408c6cSclaudio ISCSI_PDU_OPCODE(lresp->opcode)); 577cb408c6cSclaudio fail: 578cb408c6cSclaudio conn_fail(c); 57964d8b5c8Sclaudio pdu_free(p); 58064d8b5c8Sclaudio return; 581cb408c6cSclaudio } 5826727bd69Sclaudio conn_task_cleanup(c, t); 583cb408c6cSclaudio free(t); 584cb408c6cSclaudio pdu_free(p); 585cb408c6cSclaudio } 586cb408c6cSclaudio 587cb408c6cSclaudio void 588cb408c6cSclaudio initiator_logout_cb(struct connection *c, void *arg, struct pdu *p) 589cb408c6cSclaudio { 590cb408c6cSclaudio struct task_logout *tl = arg; 591cb408c6cSclaudio struct iscsi_pdu_logout_response *loresp; 592cb408c6cSclaudio 593cb408c6cSclaudio loresp = pdu_getbuf(p, NULL, PDU_HEADER); 594cb408c6cSclaudio log_debug("initiator_logout_cb: " 59512bd4d7fSclaudio "response %d, Time2Wait %d, Time2Retain %d", 596daed3c83Sclaudio loresp->response, ntohs(loresp->time2wait), 597daed3c83Sclaudio ntohs(loresp->time2retain)); 598cb408c6cSclaudio 599cb408c6cSclaudio switch (loresp->response) { 600cb408c6cSclaudio case ISCSI_LOGOUT_RESP_SUCCESS: 60112bd4d7fSclaudio if (tl->reason == ISCSI_LOGOUT_CLOSE_SESS) { 60212bd4d7fSclaudio conn_fsm(c, CONN_EV_LOGGED_OUT); 6030908bd10Sclaudio session_fsm(&c->session->sev, SESS_EV_CLOSED, 0); 60412bd4d7fSclaudio } else { 605cb408c6cSclaudio conn_fsm(tl->c, CONN_EV_LOGGED_OUT); 6060908bd10Sclaudio session_fsm(&tl->c->sev, SESS_EV_CONN_CLOSED, 0); 60712bd4d7fSclaudio } 608cb408c6cSclaudio break; 609cb408c6cSclaudio case ISCSI_LOGOUT_RESP_UNKN_CID: 610cb408c6cSclaudio /* connection ID not found, retry will not help */ 611cb408c6cSclaudio log_warnx("%s: logout failed, cid %d unknown, giving up\n", 612cb408c6cSclaudio tl->c->session->config.SessionName, 613cb408c6cSclaudio tl->c->cid); 61412bd4d7fSclaudio conn_fsm(tl->c, CONN_EV_FREE); 615cb408c6cSclaudio break; 616cb408c6cSclaudio case ISCSI_LOGOUT_RESP_NO_SUPPORT: 617cb408c6cSclaudio case ISCSI_LOGOUT_RESP_ERROR: 618cb408c6cSclaudio default: 619cb408c6cSclaudio /* need to retry logout after loresp->time2wait secs */ 620cb408c6cSclaudio conn_fail(tl->c); 62164d8b5c8Sclaudio pdu_free(p); 62264d8b5c8Sclaudio return; 623cb408c6cSclaudio } 624cb408c6cSclaudio 6256727bd69Sclaudio conn_task_cleanup(c, &tl->task); 626cb408c6cSclaudio free(tl); 627cb408c6cSclaudio pdu_free(p); 628cb408c6cSclaudio } 629cb408c6cSclaudio 630bde1ae23Sclaudio char * 631bde1ae23Sclaudio default_initiator_name(void) 632bde1ae23Sclaudio { 633ea0867c0Sderaadt char *s, hostname[HOST_NAME_MAX+1]; 634bde1ae23Sclaudio 635bde1ae23Sclaudio if (gethostname(hostname, sizeof(hostname))) 636bde1ae23Sclaudio strlcpy(hostname, "initiator", sizeof(hostname)); 637bde1ae23Sclaudio if ((s = strchr(hostname, '.'))) 638bde1ae23Sclaudio *s = '\0'; 639bde1ae23Sclaudio if (asprintf(&s, "%s:%s", ISCSID_BASE_NAME, hostname) == -1) 640bde1ae23Sclaudio return ISCSID_BASE_NAME ":initiator"; 641bde1ae23Sclaudio return s; 642bde1ae23Sclaudio } 643