xref: /openbsd-src/usr.sbin/iscsid/connection.c (revision 2224f3a16d1f18cce3c29a4d93c68c31af83a07e)
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