diff options
author | claudio <claudio@openbsd.org> | 2011-05-04 21:00:04 +0000 |
---|---|---|
committer | claudio <claudio@openbsd.org> | 2011-05-04 21:00:04 +0000 |
commit | 4125a3c444412a108755d89754375f54afdd2e56 (patch) | |
tree | 29403a986c05e491576e615f57b0f1c09c7a7b87 /usr.sbin/iscsid | |
parent | When printing scsi device ids, skip leading blanks and collapse multiple (diff) | |
download | wireguard-openbsd-4125a3c444412a108755d89754375f54afdd2e56.tar.xz wireguard-openbsd-4125a3c444412a108755d89754375f54afdd2e56.zip |
Massive diff to handle logins more correctly. iscsid will now do
better operational parameter negotiation but more is needed.
Tested by todd@ and myself.
Diffstat (limited to 'usr.sbin/iscsid')
-rw-r--r-- | usr.sbin/iscsid/connection.c | 108 | ||||
-rw-r--r-- | usr.sbin/iscsid/initiator.c | 452 | ||||
-rw-r--r-- | usr.sbin/iscsid/iscsid.c | 61 | ||||
-rw-r--r-- | usr.sbin/iscsid/iscsid.h | 41 | ||||
-rw-r--r-- | usr.sbin/iscsid/pdu.c | 91 | ||||
-rw-r--r-- | usr.sbin/iscsid/session.c | 6 | ||||
-rw-r--r-- | usr.sbin/iscsid/vscsi.c | 6 |
7 files changed, 567 insertions, 198 deletions
diff --git a/usr.sbin/iscsid/connection.c b/usr.sbin/iscsid/connection.c index 6102ddd3110..dc63a3ee187 100644 --- a/usr.sbin/iscsid/connection.c +++ b/usr.sbin/iscsid/connection.c @@ -1,4 +1,4 @@ -/* $OpenBSD: connection.c,v 1.12 2011/05/02 06:32:56 claudio Exp $ */ +/* $OpenBSD: connection.c,v 1.13 2011/05/04 21:00:04 claudio Exp $ */ /* * Copyright (c) 2009 Claudio Jeker <claudio@openbsd.org> @@ -30,6 +30,7 @@ #include <event.h> #include <stdio.h> #include <stdlib.h> +#include <strings.h> #include <unistd.h> #include "iscsid.h" @@ -62,6 +63,12 @@ conn_new(struct session *s, struct connection_config *cc) c->session = s; c->cid = arc4random(); c->config = *cc; + c->mine = initiator_conn_defaults; + c->mine.HeaderDigest = s->config.HeaderDigest; + c->mine.DataDigest = s->config.DataDigest; + c->his = iscsi_conn_defaults; + c->active = iscsi_conn_defaults; + TAILQ_INIT(&c->pdu_w); TAILQ_INIT(&c->tasks); TAILQ_INSERT_TAIL(&s->connections, c, entry); @@ -226,7 +233,6 @@ conn_task_schedule(struct connection *c) void conn_task_cleanup(struct connection *c, struct task *t) { -/* XXX THIS FEELS WRONG FOR NOW */ pdu_free_queue(&t->sendq); pdu_free_queue(&t->recvq); /* XXX need some state to know if queued or not */ @@ -239,6 +245,104 @@ conn_task_cleanup(struct connection *c, struct task *t) } } +#define SET_NUM(p, x, v, min, max) \ +do { \ + if (!strcmp((p)->key, #v)) { \ + (x)->his.v = text_to_num((p)->value, (min), (max), &err); \ + if (err) { \ + log_warnx("bad param %s=%s: %s", \ + (p)->key, (p)->value, err); \ + errors++; \ + } \ +log_debug("SET_NUM: %s = %llu", #v, (u_int64_t)(x)->his.v); \ + } \ +} while (0) + +#define SET_BOOL(p, x, v) \ +do { \ + if (!strcmp((p)->key, #v)) { \ + (x)->his.v = text_to_bool((p)->value, &err); \ + if (err) { \ + log_warnx("bad param %s=%s: %s", \ + (p)->key, (p)->value, err); \ + errors++; \ + } \ +log_debug("SET_BOOL: %s = %u", #v, (int)(x)->his.v); \ + } \ +} while (0) + +int +conn_parse_kvp(struct connection *c, struct kvp *kvp) +{ + struct kvp *k; + struct session *s = c->session; + const char *err; + int errors = 0; + + + for (k = kvp; k->key; k++) { + SET_NUM(k, s, MaxBurstLength, 512, 16777215); + SET_NUM(k, s, FirstBurstLength, 512, 16777215); + SET_NUM(k, s, DefaultTime2Wait, 0, 3600); + SET_NUM(k, s, DefaultTime2Retain, 0, 3600); + SET_NUM(k, s, MaxOutstandingR2T, 1, 65535); + SET_NUM(k, s, TargetPortalGroupTag, 1, 65535); + SET_NUM(k, s, MaxConnections, 1, 65535); + SET_BOOL(k, s, InitialR2T); + SET_BOOL(k, s, ImmediateData); + SET_BOOL(k, s, DataPDUInOrder); + SET_BOOL(k, s, DataSequenceInOrder); + SET_NUM(k, s, ErrorRecoveryLevel, 0, 2); + SET_NUM(k, c, MaxRecvDataSegmentLength, 512, 16777215); + } + + if (errors) { + log_warnx("conn_parse_kvp: errors found"); + return -1; + } + return 0; +} + +#undef SET_NUM +#undef SET_BOOL + +int +conn_gen_kvp(struct connection *c, struct kvp *kvp, size_t *nkvp) +{ + struct session *s = c->session; + size_t i = 0; + + if (s->mine.MaxConnections != iscsi_sess_defaults.MaxConnections) { + i++; + if (kvp && i < *nkvp) { + kvp[i].key = strdup("MaxConnections"); + if (kvp[i].key == NULL) + return (-1); + if (asprintf(&kvp[i].value, "%u", + (unsigned int)s->mine.MaxConnections) == -1) { + kvp[i].value = NULL; + return (-1); + } + } + } + if (c->mine.MaxRecvDataSegmentLength != + iscsi_conn_defaults.MaxRecvDataSegmentLength) { + i++; + if (kvp && i < *nkvp) { + kvp[i].key = strdup("MaxRecvDataSegmentLength"); + if (kvp[i].key == NULL) + return (-1); + if (asprintf(&kvp[i].value, "%u", + (unsigned int)c->mine.MaxRecvDataSegmentLength) == -1) { + kvp[i].value = NULL; + return (-1); + } + } + } + + *nkvp = i; + return (0); +} void conn_pdu_write(struct connection *c, struct pdu *p) diff --git a/usr.sbin/iscsid/initiator.c b/usr.sbin/iscsid/initiator.c index 7510baffed4..027b3c4103d 100644 --- a/usr.sbin/iscsid/initiator.c +++ b/usr.sbin/iscsid/initiator.c @@ -1,4 +1,4 @@ -/* $OpenBSD: initiator.c,v 1.8 2011/05/02 06:32:56 claudio Exp $ */ +/* $OpenBSD: initiator.c,v 1.9 2011/05/04 21:00:04 claudio Exp $ */ /* * Copyright (c) 2009 Claudio Jeker <claudio@openbsd.org> @@ -35,7 +35,32 @@ struct initiator *initiator; -struct kvp *initiator_login_kvp(struct session *); +struct task_login { + struct task task; + struct connection *c; + u_int16_t tsih; + u_int8_t stage; +}; + +struct task_logout { + struct task task; + struct connection *c; + u_int8_t reason; +}; + +struct kvp *initiator_login_kvp(struct connection *, u_int8_t); +struct pdu *initiator_login_build(struct connection *, + struct task_login *); +struct pdu *initiator_text_build(struct task *, struct session *, + struct kvp *); + +void initiator_login_cb(struct connection *, void *, struct pdu *); +void initiator_discovery_cb(struct connection *, void *, struct pdu *); +void initiator_logout_cb(struct connection *, void *, struct pdu *); + + +struct session_params initiator_sess_defaults; +struct connection_params initiator_conn_defaults; struct initiator * initiator_init(void) @@ -47,6 +72,13 @@ initiator_init(void) arc4random_uniform(0xffffff) | ISCSI_ISID_RAND; initiator->config.isid_qual = arc4random_uniform(0xffff); TAILQ_INIT(&initiator->sessions); + + /* initialize initiator defaults */ + initiator_sess_defaults = iscsi_sess_defaults; + initiator_conn_defaults = iscsi_conn_defaults; + initiator_sess_defaults.MaxConnections = 8; + initiator_conn_defaults.MaxRecvDataSegmentLength = 65536; + return initiator; } @@ -98,104 +130,186 @@ initiator_t2s(u_int target) return NULL; } -struct task_login { - struct task task; - struct connection *c; - u_int16_t tsih; - u_int8_t stage; -}; - -struct task_logout { - struct task task; - struct connection *c; - u_int8_t reason; -}; +void +initiator_login(struct connection *c) +{ + struct task_login *tl; + struct pdu *p; -struct pdu *initiator_login_build(struct task_login *, struct kvp *); -void initiator_login_cb(struct connection *, void *, struct pdu *); + if (!(tl = calloc(1, sizeof(*tl)))) { + log_warn("initiator_login"); + conn_fail(c); + return; + } + tl->c = c; + tl->stage = ISCSI_LOGIN_STG_SECNEG; -void initiator_discovery_cb(struct connection *, void *, struct pdu *); -struct pdu *initiator_text_build(struct task *, struct session *, struct kvp *); + if (!(p = initiator_login_build(c, tl))) { + log_warn("initiator_login_build failed"); + free(tl); + conn_fail(c); + return; + } -void initiator_logout_cb(struct connection *, void *, struct pdu *); + task_init(&tl->task, c->session, 1, tl, initiator_login_cb, NULL); + task_pdu_add(&tl->task, p); + conn_task_issue(c, &tl->task); +} -struct kvp * -initiator_login_kvp(struct session *s) +void +initiator_discovery(struct session *s) { - struct kvp *kvp; + struct task *t; + struct pdu *p; + struct kvp kvp[] = { + { "SendTargets", "All" }, + { NULL, NULL } + }; - if (!(kvp = calloc(4, sizeof(*kvp)))) - return NULL; - kvp[0].key = "AuthMethod"; - kvp[0].value = "None"; - kvp[1].key = "InitiatorName"; - kvp[1].value = s->config.InitiatorName; - - if (s->config.SessionType == SESSION_TYPE_DISCOVERY) { - kvp[2].key = "SessionType"; - kvp[2].value = "Discovery"; - } else { - kvp[2].key = "TargetName"; - kvp[2].value = s->config.TargetName; + if (!(t = calloc(1, sizeof(*t)))) { + log_warn("initiator_discovery"); + /* XXX sess_fail(c); */ + return; } - return kvp; + if (!(p = initiator_text_build(t, s, kvp))) { + log_warnx("initiator_text_build failed"); + free(t); + /* XXX sess_fail(c); */ + return; + } + + task_init(t, s, 0, t, initiator_discovery_cb, NULL); + task_pdu_add(t, p); + session_task_issue(s, t); } void -initiator_login(struct connection *c) +initiator_logout(struct session *s, struct connection *c, u_int8_t reason) { - struct task_login *tl; + struct task_logout *tl; struct pdu *p; - struct kvp *kvp; + struct iscsi_pdu_logout_request *loreq; if (!(tl = calloc(1, sizeof(*tl)))) { - log_warn("initiator_login"); - conn_fail(c); + log_warn("initiator_logout"); + /* XXX sess_fail */ return; } tl->c = c; - tl->stage = ISCSI_LOGIN_STG_SECNEG; + tl->reason = reason; - if (!(kvp = initiator_login_kvp(c->session))) { - log_warn("initiator_login_kvp failed"); + if (!(p = pdu_new())) { + log_warn("initiator_logout"); + /* XXX sess_fail */ free(tl); - conn_fail(c); return; } - - if (!(p = initiator_login_build(tl, kvp))) { - log_warn("initiator_login_build failed"); + if (!(loreq = pdu_gethdr(p))) { + log_warn("initiator_logout"); + /* XXX sess_fail */ + pdu_free(p); free(tl); - free(kvp); - conn_fail(c); return; } - free(kvp); + loreq->opcode = ISCSI_OP_LOGOUT_REQUEST; + loreq->flags = ISCSI_LOGOUT_F | reason; + if (reason != ISCSI_LOGOUT_CLOSE_SESS) + loreq->cid = c->cid; - task_init(&tl->task, c->session, 1, tl, initiator_login_cb, NULL); + task_init(&tl->task, s, 0, tl, initiator_logout_cb, NULL); task_pdu_add(&tl->task, p); - conn_task_issue(c, &tl->task); + if (c && (c->state & CONN_RUNNING)) + conn_task_issue(c, &tl->task); + else + session_logout_issue(s, &tl->task); +} + +void +initiator_nop_in_imm(struct connection *c, struct pdu *p) +{ + struct iscsi_pdu_nop_in *nopin; + struct task *t; + + /* fixup NOP-IN to make it a NOP-OUT */ + nopin = pdu_getbuf(p, NULL, PDU_HEADER); + nopin->maxcmdsn = 0; + nopin->opcode = ISCSI_OP_I_NOP | ISCSI_OP_F_IMMEDIATE; + + /* and schedule an immediate task */ + if (!(t = calloc(1, sizeof(*t)))) { + log_warn("initiator_nop_in_imm"); + pdu_free(p); + return; + } + + task_init(t, c->session, 1, NULL, NULL, NULL); + t->itt = 0xffffffff; /* change ITT because it is just a ping reply */ + task_pdu_add(t, p); + conn_task_issue(c, t); +} + +struct kvp * +initiator_login_kvp(struct connection *c, u_int8_t stage) +{ + struct kvp *kvp; + size_t nkvp; + + switch (stage) { + case ISCSI_LOGIN_STG_SECNEG: + if (!(kvp = calloc(4, sizeof(*kvp)))) + return NULL; + kvp[0].key = "AuthMethod"; + kvp[0].value = "None"; + kvp[1].key = "InitiatorName"; + kvp[1].value = c->session->config.InitiatorName; + + if (c->session->config.SessionType == SESSION_TYPE_DISCOVERY) { + kvp[2].key = "SessionType"; + kvp[2].value = "Discovery"; + } else { + kvp[2].key = "TargetName"; + kvp[2].value = c->session->config.TargetName; + } + break; + case ISCSI_LOGIN_STG_OPNEG: + if (conn_gen_kvp(c, NULL, &nkvp) == -1) + return NULL; + if (!(kvp = calloc(nkvp, sizeof(*kvp)))) + return NULL; + if (conn_gen_kvp(c, kvp, &nkvp) == -1) { + free(kvp); + return NULL; + } + break; + default: + log_warnx("initiator_login_kvp: exit stage left"); + return NULL; + } + return kvp; } struct pdu * -initiator_login_build(struct task_login *tl, struct kvp *kvp) +initiator_login_build(struct connection *c, struct task_login *tl) { struct pdu *p; + struct kvp *kvp; struct iscsi_pdu_login_request *lreq; int n; if (!(p = pdu_new())) return NULL; - if (!(lreq = pdu_gethdr(p))) + if (!(lreq = pdu_gethdr(p))) { + pdu_free(p); return NULL; + } lreq->opcode = ISCSI_OP_LOGIN_REQUEST | ISCSI_OP_F_IMMEDIATE; if (tl->stage == ISCSI_LOGIN_STG_SECNEG) lreq->flags = ISCSI_LOGIN_F_T | - ISCSI_LOGIN_F_CSG(ISCSI_LOGIN_STG_OPNEG) | - ISCSI_LOGIN_F_NSG(ISCSI_LOGIN_STG_FULL); + ISCSI_LOGIN_F_CSG(ISCSI_LOGIN_STG_SECNEG) | + ISCSI_LOGIN_F_NSG(ISCSI_LOGIN_STG_OPNEG); else if (tl->stage == ISCSI_LOGIN_STG_OPNEG) lreq->flags = ISCSI_LOGIN_F_T | ISCSI_LOGIN_F_CSG(ISCSI_LOGIN_STG_OPNEG) | @@ -207,6 +321,44 @@ initiator_login_build(struct task_login *tl, struct kvp *kvp) lreq->cid = htons(tl->c->cid); lreq->expstatsn = htonl(tl->c->expstatsn); + if (!(kvp = initiator_login_kvp(c, tl->stage))) { + log_warn("initiator_login_kvp failed"); + return NULL; + } + if ((n = text_to_pdu(kvp, p)) == -1) { + free(kvp); + return NULL; + } + free(kvp); + + if (n > 8192) { + log_warn("initiator_login_build: help, I'm too verbose"); + pdu_free(p); + return NULL; + } + n = htonl(n); + /* copy 32bit value over ahslen and datalen */ + bcopy(&n, &lreq->ahslen, sizeof(n)); + + return p; +} + +struct pdu * +initiator_text_build(struct task *t, struct session *s, struct kvp *kvp) +{ + struct pdu *p; + struct iscsi_pdu_text_request *lreq; + int n; + + if (!(p = pdu_new())) + return NULL; + if (!(lreq = pdu_gethdr(p))) + return NULL; + + lreq->opcode = ISCSI_OP_TEXT_REQUEST; + lreq->flags = ISCSI_TEXT_F_F; + lreq->ttt = 0xffffffff; + if ((n = text_to_pdu(kvp, p)) == -1) return NULL; n = htonl(n); @@ -220,46 +372,82 @@ initiator_login_cb(struct connection *c, void *arg, struct pdu *p) { struct task_login *tl = arg; struct iscsi_pdu_login_response *lresp; + u_char *buf = NULL; + struct kvp *kvp; + size_t n, size; lresp = pdu_getbuf(p, NULL, PDU_HEADER); - /* XXX handle packet would be great */ - log_pdu(p, 1); + if (ISCSI_PDU_OPCODE(lresp->opcode) != ISCSI_OP_LOGIN_RESPONSE) { - log_debug("Unknown crap"); + log_warnx("Unexpected login response type %x", + ISCSI_PDU_OPCODE(lresp->opcode)); + conn_fail(c); + goto done; } - conn_task_cleanup(c, &tl->task); - conn_fsm(c, CONN_EV_LOGGED_IN); - free(tl); - pdu_free(p); -} - -void -initiator_discovery(struct session *s) -{ - struct task *t; - struct pdu *p; - struct kvp kvp[] = { - { "SendTargets", "All" }, - { NULL, NULL } - }; + if (lresp->flags & ISCSI_LOGIN_F_C) { + log_warnx("Incomplete login responses are unsupported"); + conn_fail(c); + goto done; + } - if (!(t = calloc(1, sizeof(*t)))) { - log_warn("initiator_discovery"); - /* XXX conn_fail(c); */ - return; + size = lresp->datalen[0] << 16 | lresp->datalen[1] << 8 | + lresp->datalen[2]; + buf = pdu_getbuf(p, &n, PDU_DATA); + if (size > n) { + log_warnx("Bad login response"); + conn_fail(c); + goto done; } - if (!(p = initiator_text_build(t, s, kvp))) { - log_warnx("initiator_text_build failed"); - free(t); - /* conn_fail(c); */ - return; + if (buf) { + kvp = pdu_to_text(buf, size); + if (kvp == NULL) { + conn_fail(c); + goto done; + } + + if (conn_parse_kvp(c, kvp) == -1) { + free(kvp); + conn_fail(c); + goto done; + } + free(kvp); } - task_init(t, s, 0, t, initiator_discovery_cb, NULL); - task_pdu_add(t, p); - session_task_issue(s, t); + /* advance FSM if possible */ + if (lresp->flags & ISCSI_LOGIN_F_T) + tl->stage = ISCSI_LOGIN_F_NSG(lresp->flags); + + switch (tl->stage) { + case ISCSI_LOGIN_STG_SECNEG: + case ISCSI_LOGIN_STG_OPNEG: + /* free no longer used pdu */ + pdu_free(p); + p = initiator_login_build(c, tl); + if (p == NULL) { + conn_fail(c); + goto done; + } + break; + case ISCSI_LOGIN_STG_FULL: + conn_fsm(c, CONN_EV_LOGGED_IN); + goto done; + default: + log_warnx("initiator_login_cb: exit stage left"); + conn_fail(c); + goto done; + } + conn_task_cleanup(c, &tl->task); + /* add new pdu and re-issue the task */ + task_pdu_add(&tl->task, p); + conn_task_issue(c, &tl->task); + return; +done: + conn_task_cleanup(c, &tl->task); + free(tl); + if (p) + pdu_free(p); } void @@ -306,48 +494,6 @@ fail: } void -initiator_logout(struct session *s, struct connection *c, u_int8_t reason) -{ - struct task_logout *tl; - struct pdu *p; - struct iscsi_pdu_logout_request *loreq; - - if (!(tl = calloc(1, sizeof(*tl)))) { - log_warn("initiator_logout"); - /* XXX sess_fail */ - return; - } - tl->c = c; - tl->reason = reason; - - if (!(p = pdu_new())) { - log_warn("initiator_logout"); - /* XXX sess_fail */ - free(tl); - return; - } - if (!(loreq = pdu_gethdr(p))) { - log_warn("initiator_logout"); - /* XXX sess_fail */ - pdu_free(p); - free(tl); - return; - } - - loreq->opcode = ISCSI_OP_LOGOUT_REQUEST; - loreq->flags = ISCSI_LOGOUT_F | reason; - if (reason != ISCSI_LOGOUT_CLOSE_SESS) - loreq->cid = c->cid; - - task_init(&tl->task, s, 0, tl, initiator_logout_cb, NULL); - task_pdu_add(&tl->task, p); - if (c && (c->state & CONN_RUNNING)) - conn_task_issue(c, &tl->task); - else - session_logout_issue(s, &tl->task); -} - -void initiator_logout_cb(struct connection *c, void *arg, struct pdu *p) { struct task_logout *tl = arg; @@ -388,54 +534,6 @@ initiator_logout_cb(struct connection *c, void *arg, struct pdu *p) pdu_free(p); } -void -initiator_nop_in_imm(struct connection *c, struct pdu *p) -{ - struct iscsi_pdu_nop_in *nopin; - struct task *t; - - /* fixup NOP-IN to make it a NOP-OUT */ - nopin = pdu_getbuf(p, NULL, PDU_HEADER); - nopin->maxcmdsn = 0; - nopin->opcode = ISCSI_OP_I_NOP | ISCSI_OP_F_IMMEDIATE; - - /* and schedule an immediate task */ - if (!(t = calloc(1, sizeof(*t)))) { - log_warn("initiator_nop_in_imm"); - pdu_free(p); - return; - } - - task_init(t, c->session, 1, NULL, NULL, NULL); - t->itt = 0xffffffff; /* change ITT because it is just a ping reply */ - task_pdu_add(t, p); - conn_task_issue(c, t); -} - -struct pdu * -initiator_text_build(struct task *t, struct session *s, struct kvp *kvp) -{ - struct pdu *p; - struct iscsi_pdu_text_request *lreq; - int n; - - if (!(p = pdu_new())) - return NULL; - if (!(lreq = pdu_gethdr(p))) - return NULL; - - lreq->opcode = ISCSI_OP_TEXT_REQUEST; - lreq->flags = ISCSI_TEXT_F_F; - lreq->ttt = 0xffffffff; - - if ((n = text_to_pdu(kvp, p)) == -1) - return NULL; - n = htonl(n); - bcopy(&n, &lreq->ahslen, sizeof(n)); - - return p; -} - char * default_initiator_name(void) { diff --git a/usr.sbin/iscsid/iscsid.c b/usr.sbin/iscsid/iscsid.c index 1989c5775ea..28dc39c6d25 100644 --- a/usr.sbin/iscsid/iscsid.c +++ b/usr.sbin/iscsid/iscsid.c @@ -1,4 +1,4 @@ -/* $OpenBSD: iscsid.c,v 1.6 2011/05/02 06:32:56 claudio Exp $ */ +/* $OpenBSD: iscsid.c,v 1.7 2011/05/04 21:00:04 claudio Exp $ */ /* * Copyright (c) 2009 Claudio Jeker <claudio@openbsd.org> @@ -43,6 +43,24 @@ struct event exit_ev; int exit_rounds; #define ISCSI_EXIT_WAIT 5 +const struct session_params iscsi_sess_defaults = { + .MaxBurstLength = 262144, + .FirstBurstLength = 65536, + .DefaultTime2Wait = 2, + .DefaultTime2Retain = 20, + .MaxOutstandingR2T = 1, + .MaxConnections = 1, + .InitialR2T = 1, + .ImmediateData = 1, + .DataPDUInOrder = 1, + .DataSequenceInOrder = 1, + .ErrorRecoveryLevel = 0 +}; + +const struct connection_params iscsi_conn_defaults = { + .MaxRecvDataSegmentLength = 8192 +}; + int main(int argc, char *argv[]) { @@ -252,3 +270,44 @@ shutdown_cb(int fd, short event, void *arg) if (evtimer_add(&exit_ev, &tv) == -1) fatal("shutdown_cb"); } + +#define MERGE_MIN(r, a, b, v) \ + res->v = (mine->v < his->v ? mine->v : his->v) +#define MERGE_MAX(r, a, b, v) \ + res->v = (mine->v > his->v ? mine->v : his->v) +#define MERGE_OR(r, a, b, v) \ + res->v = (mine->v || his->v) +#define MERGE_AND(r, a, b, v) \ + res->v = (mine->v && his->v) + +void +iscsi_merge_sess_params(struct session_params *res, + struct session_params *mine, struct session_params *his) +{ + MERGE_MIN(res, mine, his, MaxBurstLength); + MERGE_MIN(res, mine, his, FirstBurstLength); + MERGE_MAX(res, mine, his, DefaultTime2Wait); + MERGE_MIN(res, mine, his, DefaultTime2Retain); + MERGE_MIN(res, mine, his, MaxOutstandingR2T); + res->TargetPortalGroupTag = his->TargetPortalGroupTag; + MERGE_MIN(res, mine, his, MaxConnections); + MERGE_OR(res, mine, his, InitialR2T); + MERGE_AND(res, mine, his, ImmediateData); + MERGE_OR(res, mine, his, DataPDUInOrder); + MERGE_OR(res, mine, his, DataSequenceInOrder); + MERGE_MIN(res, mine, his, ErrorRecoveryLevel); + +} + +void +iscsi_merge_conn_params(struct connection_params *res, + struct connection_params *mine, struct connection_params *his) +{ + res->MaxRecvDataSegmentLength = his->MaxRecvDataSegmentLength; + /* XXX HeaderDigest and DataDigest */ +} + +#undef MERGE_MIN +#undef MERGE_MAX +#undef MERGE_OR +#undef MERGE_AND diff --git a/usr.sbin/iscsid/iscsid.h b/usr.sbin/iscsid/iscsid.h index 42ec7c5237c..469adbef1bf 100644 --- a/usr.sbin/iscsid/iscsid.h +++ b/usr.sbin/iscsid/iscsid.h @@ -1,4 +1,4 @@ -/* $OpenBSD: iscsid.h,v 1.8 2011/05/02 06:32:56 claudio Exp $ */ +/* $OpenBSD: iscsid.h,v 1.9 2011/05/04 21:00:04 claudio Exp $ */ /* * Copyright (c) 2009 Claudio Jeker <claudio@openbsd.org> @@ -22,6 +22,7 @@ #define ISCSID_USER "_iscsid" #define ISCSID_BASE_NAME "iqn.1995-11.org.openbsd.iscsid" +#define ISCSID_DEF_CONNS 8 #define PDU_READ_SIZE (256 * 1024) #define CONTROL_READ_SIZE 8192 @@ -73,19 +74,20 @@ TAILQ_HEAD(taskq, task); #define SESS_ANYSTATE 0xffff #define SESS_RUNNING (SESS_FREE | SESS_LOGGED_IN | SESS_FAILED) -#define CONN_FREE 0x0001 -#define CONN_XPT_WAIT 0x0002 -#define CONN_XPT_UP 0x0004 -#define CONN_IN_LOGIN 0x0008 -#define CONN_LOGGED_IN 0x0010 -#define CONN_IN_LOGOUT 0x0020 -#define CONN_LOGOUT_REQ 0x0040 -#define CONN_CLEANUP_WAIT 0x0080 -#define CONN_IN_CLEANUP 0x0100 +#define CONN_FREE 0x0001 +#define CONN_XPT_WAIT 0x0002 +#define CONN_XPT_UP 0x0004 +#define CONN_IN_LOGIN 0x0008 +#define CONN_LOGGED_IN 0x0010 +#define CONN_IN_LOGOUT 0x0020 +#define CONN_LOGOUT_REQ 0x0040 +#define CONN_CLEANUP_WAIT 0x0080 +#define CONN_IN_CLEANUP 0x0100 #define CONN_ANYSTATE 0xffff #define CONN_RUNNING (CONN_LOGGED_IN | CONN_LOGOUT_REQ) #define CONN_FAILED (CONN_CLEANUP_WAIT | CONN_IN_CLEANUP) -#define CONN_NEVER_LOGGED_IN (CONN_FREE | CONN_XPT_WAIT | CONN_XPT_UP) +#define CONN_NEVER_LOGGED_IN (CONN_FREE | CONN_XPT_WAIT | CONN_XPT_UP | \ + CONN_IN_LOGIN) enum c_event { CONN_EV_FAIL, @@ -177,7 +179,7 @@ struct session_params { u_int16_t TargetPortalGroupTag; /* 1- 65535: IS */ u_int16_t MaxConnections; - /* 1, 1-65535 (min()): LO */ + /* 1, 1-65535 (min()): LS */ u_int8_t InitialR2T; /* yes, bool (||): LS */ u_int8_t ImmediateData; @@ -269,7 +271,16 @@ struct kvp { #define KVP_KEY_ALLOCED 0x01 #define KVP_VALUE_ALLOCED 0x02 +extern const struct session_params iscsi_sess_defaults; +extern const struct connection_params iscsi_conn_defaults; +extern struct session_params initiator_sess_defaults; +extern struct connection_params initiator_conn_defaults; + void iscsid_ctrl_dispatch(void *, struct pdu *); +void iscsi_merge_sess_params(struct session_params *, + struct session_params *, struct session_params *); +void iscsi_merge_conn_params(struct connection_params *, + struct connection_params *, struct connection_params *); struct initiator *initiator_init(void); void initiator_cleanup(struct initiator *); @@ -304,7 +315,9 @@ void conn_free(struct connection *); int conn_task_ready(struct connection *); void conn_task_issue(struct connection *, struct task *); void conn_task_schedule(struct connection *); -void conn_task_cleanup(struct connection *c, struct task *); +void conn_task_cleanup(struct connection *, struct task *); +int conn_parse_kvp(struct connection *, struct kvp *); +int conn_gen_kvp(struct connection *, struct kvp *, size_t *); void conn_pdu_write(struct connection *, struct pdu *); void conn_fail(struct connection *); void conn_fsm(struct connection *, enum c_event); @@ -312,6 +325,8 @@ void conn_fsm(struct connection *, enum c_event); void *pdu_gethdr(struct pdu *); int text_to_pdu(struct kvp *, struct pdu *); struct kvp *pdu_to_text(char *, size_t); +u_int64_t text_to_num(const char *, u_int64_t, u_int64_t, const char **); +int text_to_bool(const char *, const char **); void pdu_free_queue(struct pduq *); ssize_t pdu_read(struct connection *); ssize_t pdu_write(struct connection *); diff --git a/usr.sbin/iscsid/pdu.c b/usr.sbin/iscsid/pdu.c index 759389b59b6..57a5c21bcdd 100644 --- a/usr.sbin/iscsid/pdu.c +++ b/usr.sbin/iscsid/pdu.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pdu.c,v 1.5 2011/04/27 07:25:26 claudio Exp $ */ +/* $OpenBSD: pdu.c,v 1.6 2011/05/04 21:00:04 claudio Exp $ */ /* * Copyright (c) 2009 Claudio Jeker <claudio@openbsd.org> @@ -24,6 +24,7 @@ #include <errno.h> #include <event.h> +#include <limits.h> #include <stdio.h> #include <stdlib.h> #include <strings.h> @@ -58,6 +59,9 @@ text_to_pdu(struct kvp *k, struct pdu *p) size_t len = 0, rem; int n, nk; + if (k == NULL) + return (0); + nk = 0; while(k[nk].key) { len += 2 + strlen(k[nk].key) + strlen(k[nk].value); @@ -122,6 +126,91 @@ pdu_to_text(char *buf, size_t len) return k; } +/* Modified version of strtonum() to fit iscsid's need + * + * Copyright (c) 2004 Ted Unangst and Todd Miller + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +u_int64_t +text_to_num(const char *numstr, u_int64_t minval, u_int64_t maxval, + const char **errstrp) +{ + unsigned long long ull = 0; + char *ep; + int error = 0; + struct errval { + const char *errstr; + int err; + } ev[4] = { + { NULL, 0 }, + { "invalid", EINVAL }, + { "too small", ERANGE }, + { "too large", ERANGE } + }; +#define INVALID 1 +#define TOOSMALL 2 +#define TOOLARGE 3 + + ev[0].err = errno; + errno = 0; + if (minval > maxval) + error = INVALID; + else { + ull = strtoull(numstr, &ep, 0); + if (numstr == ep || *ep != '\0') + error = INVALID; + else if (ull < minval) + error = TOOSMALL; + else if ((ull == ULLONG_MAX && errno == ERANGE) || ull > maxval) + error = TOOLARGE; + } + if (errstrp != NULL) + *errstrp = ev[error].errstr; + errno = ev[error].err; + if (error) + ull = 0; + + return (ull); +#undef INVALID +#undef TOOSMALL +#undef TOOLARGE +} + +int +text_to_bool(const char *buf, const char **errstrp) +{ + int val = 0; + + if (!strcmp(buf, "Yes")) { + val = 1; + errno = 0; + } else if (!strcmp(buf, "No")) + errno = 0; + else + errno = EINVAL; + + if (errstrp != NULL) { + if (errno == 0) + *errstrp = NULL; + else + *errstrp = "invalid"; + } + return (val); +} + + /* * Internal functions to send/recv pdus. */ diff --git a/usr.sbin/iscsid/session.c b/usr.sbin/iscsid/session.c index 5ce56b1fd36..d25fb7b83e7 100644 --- a/usr.sbin/iscsid/session.c +++ b/usr.sbin/iscsid/session.c @@ -1,4 +1,4 @@ -/* $OpenBSD: session.c,v 1.3 2011/05/02 06:32:56 claudio Exp $ */ +/* $OpenBSD: session.c,v 1.4 2011/05/04 21:00:04 claudio Exp $ */ /* * Copyright (c) 2011 Claudio Jeker <claudio@openbsd.org> @@ -72,6 +72,10 @@ session_new(struct initiator *i, u_int8_t st) s->itt = arc4random(); s->initiator = i; s->state = SESS_INIT; + s->mine = initiator_sess_defaults; + s->mine.MaxConnections = s->config.MaxConnections; + s->his = iscsi_sess_defaults; + s->active = iscsi_sess_defaults; if (st == SESSION_TYPE_DISCOVERY) s->target = 0; diff --git a/usr.sbin/iscsid/vscsi.c b/usr.sbin/iscsid/vscsi.c index 4f93009cbfb..35c9dd84c9a 100644 --- a/usr.sbin/iscsid/vscsi.c +++ b/usr.sbin/iscsid/vscsi.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vscsi.c,v 1.7 2011/04/28 18:25:42 claudio Exp $ */ +/* $OpenBSD: vscsi.c,v 1.8 2011/05/04 21:00:04 claudio Exp $ */ /* * Copyright (c) 2009 Claudio Jeker <claudio@openbsd.org> @@ -275,8 +275,8 @@ vscsi_dataout(struct connection *c, struct scsi_task *t, u_int32_t ttt, u_int32_t t32, dsn = 0; for (off = 0; off < len; off += size) { - /* XXX hardcoded numbers, bad bad bad */ - size = len - off > 8 * 1024 ? 8 * 1024 : len - off; + size = len - off > c->active.MaxRecvDataSegmentLength ? + c->active.MaxRecvDataSegmentLength : len - off; if (!(p = pdu_new())) fatal("vscsi_r2t"); |