summaryrefslogtreecommitdiffstats
path: root/usr.sbin/iscsid
diff options
context:
space:
mode:
authorclaudio <claudio@openbsd.org>2011-05-04 21:00:04 +0000
committerclaudio <claudio@openbsd.org>2011-05-04 21:00:04 +0000
commit4125a3c444412a108755d89754375f54afdd2e56 (patch)
tree29403a986c05e491576e615f57b0f1c09c7a7b87 /usr.sbin/iscsid
parentWhen printing scsi device ids, skip leading blanks and collapse multiple (diff)
downloadwireguard-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.c108
-rw-r--r--usr.sbin/iscsid/initiator.c452
-rw-r--r--usr.sbin/iscsid/iscsid.c61
-rw-r--r--usr.sbin/iscsid/iscsid.h41
-rw-r--r--usr.sbin/iscsid/pdu.c91
-rw-r--r--usr.sbin/iscsid/session.c6
-rw-r--r--usr.sbin/iscsid/vscsi.c6
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");