xref: /openbsd-src/usr.sbin/iscsid/connection.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*	$OpenBSD: connection.c,v 1.13 2011/05/04 21:00:04 claudio Exp $ */
2 
3 /*
4  * Copyright (c) 2009 Claudio Jeker <claudio@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/queue.h>
21 #include <sys/socket.h>
22 #include <sys/uio.h>
23 
24 #include <netinet/in.h>
25 #include <netinet/tcp.h>
26 
27 #include <scsi/iscsi.h>
28 
29 #include <errno.h>
30 #include <event.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <strings.h>
34 #include <unistd.h>
35 
36 #include "iscsid.h"
37 #include "log.h"
38 
39 void	conn_dispatch(int, short, void *);
40 void	conn_write_dispatch(int, short, void *);
41 
42 int	c_do_connect(struct connection *, enum c_event);
43 int	c_do_login(struct connection *, enum c_event);
44 int	c_do_loggedin(struct connection *, enum c_event);
45 int	c_do_logout(struct connection *, enum c_event);
46 int	c_do_loggedout(struct connection *, enum c_event);
47 int	c_do_fail(struct connection *, enum c_event);
48 
49 const char *conn_state(int);
50 const char *conn_event(enum c_event);
51 
52 void
53 conn_new(struct session *s, struct connection_config *cc)
54 {
55 	struct connection *c;
56 	int nodelay = 1;
57 
58 	if (!(c = calloc(1, sizeof(*c))))
59 		fatal("session_add_conn");
60 
61 	c->fd = -1;
62 	c->state = CONN_FREE;
63 	c->session = s;
64 	c->cid = arc4random();
65 	c->config = *cc;
66 	c->mine = initiator_conn_defaults;
67 	c->mine.HeaderDigest = s->config.HeaderDigest;
68 	c->mine.DataDigest = s->config.DataDigest;
69 	c->his = iscsi_conn_defaults;
70 	c->active = iscsi_conn_defaults;
71 
72 	TAILQ_INIT(&c->pdu_w);
73 	TAILQ_INIT(&c->tasks);
74 	TAILQ_INSERT_TAIL(&s->connections, c, entry);
75 
76 	if (pdu_readbuf_set(&c->prbuf, PDU_READ_SIZE)) {
77 		log_warn("conn_new");
78 		conn_free(c);
79 		return;
80 	}
81 
82 	/* create socket */
83 	c->fd = socket(c->config.TargetAddr.ss_family, SOCK_STREAM, 0);
84 	if (c->fd == -1) {
85 		log_warn("conn_new: socket");
86 		conn_free(c);
87 		return;
88 	}
89 	if (socket_setblockmode(c->fd, 1)) {
90 		log_warn("conn_new: socket_setblockmode");
91 		conn_free(c);
92 		return;
93 	}
94 
95 	/* try to turn off TCP Nagle */
96 	if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, &nodelay,
97 	    sizeof(nodelay)) == -1)
98 		log_warn("conn_new: setting TCP_NODELAY");
99 
100 	event_set(&c->ev, c->fd, EV_READ|EV_PERSIST, conn_dispatch, c);
101 	event_set(&c->wev, c->fd, EV_WRITE, conn_write_dispatch, c);
102 	event_add(&c->ev, NULL);
103 
104 	conn_fsm(c, CONN_EV_CONNECT);
105 }
106 
107 void
108 conn_free(struct connection *c)
109 {
110 	pdu_readbuf_free(&c->prbuf);
111 	pdu_free_queue(&c->pdu_w);
112 
113 	event_del(&c->ev);
114 	event_del(&c->wev);
115 	close(c->fd);
116 
117 	taskq_cleanup(&c->tasks);
118 
119 	TAILQ_REMOVE(&c->session->connections, c, entry);
120 	free(c);
121 }
122 
123 void
124 conn_dispatch(int fd, short event, void *arg)
125 {
126 	struct connection *c = arg;
127 	ssize_t n;
128 
129 	if (!(event & EV_READ)) {
130 		log_debug("spurious read call");
131 		return;
132 	}
133 	if ((n = pdu_read(c)) == -1) {
134 		conn_fsm(c, CONN_EV_FAIL);
135 		return;
136 	}
137 	if (n == 0) {    /* connection closed */
138 		conn_fsm(c, CONN_EV_CLOSED);
139 		return;
140 	}
141 
142 	pdu_parse(c);
143 }
144 
145 void
146 conn_write_dispatch(int fd, short event, void *arg)
147 {
148 	struct connection *c = arg;
149 	ssize_t n;
150 	int error;
151 	socklen_t len;
152 
153 	if (!(event & EV_WRITE)) {
154 		log_debug("spurious write call");
155 		return;
156 	}
157 
158 	switch (c->state) {
159 	case CONN_XPT_WAIT:
160 		len = sizeof(error);
161 		if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR,
162 		    &error, &len) == -1 || (errno = error)) {
163 			log_warn("cwd connect(%s)",
164 			    log_sockaddr(&c->config.TargetAddr));
165 			conn_fsm(c, CONN_EV_FAIL);
166 			return;
167 		}
168 		conn_fsm(c, CONN_EV_CONNECTED);
169 		break;
170 	default:
171 		if ((n = pdu_write(c)) == -1) {
172 			log_warn("pdu_write");
173 			conn_fsm(c, CONN_EV_FAIL);
174 			return;
175 		}
176 		if (n == 0) {    /* connection closed */
177 			conn_fsm(c, CONN_EV_CLOSED);
178 			return;
179 		}
180 
181 		/* check if there is more to send */
182 		if (pdu_pending(c))
183 			event_add(&c->wev, NULL);
184 	}
185 }
186 
187 void
188 conn_fail(struct connection *c)
189 {
190 	log_debug("conn_fail");
191 	conn_fsm(c, CONN_EV_FAIL);
192 }
193 
194 int
195 conn_task_ready(struct connection *c)
196 {
197 	if ((c->state & CONN_RUNNING) && TAILQ_EMPTY(&c->tasks))
198 		return 1;
199 	return 0;
200 }
201 
202 void
203 conn_task_issue(struct connection *c, struct task *t)
204 {
205 	TAILQ_INSERT_TAIL(&c->tasks, t, entry);
206 	conn_task_schedule(c);
207 }
208 
209 void
210 conn_task_schedule(struct connection *c)
211 {
212 	struct task *t = TAILQ_FIRST(&c->tasks);
213 	struct pdu *p, *np;
214 
215 	if (!t) {
216 		log_debug("conn_task_schedule: task is hiding");
217 		return;
218 	}
219 
220 	/* move pdus to the write queue */
221 	for (p = TAILQ_FIRST(&t->sendq); p != NULL; p = np) {
222 		np = TAILQ_NEXT(p, entry);
223 		TAILQ_REMOVE(&t->sendq, p, entry);
224 		conn_pdu_write(c, p);
225 	}
226 	if (t->callback == NULL) {
227 		/* no callback, immediate command expecting no answer */
228 		conn_task_cleanup(c, t);
229 		free(t);
230 	}
231 }
232 
233 void
234 conn_task_cleanup(struct connection *c, struct task *t)
235 {
236 	pdu_free_queue(&t->sendq);
237 	pdu_free_queue(&t->recvq);
238 	/* XXX need some state to know if queued or not */
239 	if (c) {
240 		TAILQ_REMOVE(&c->tasks, t, entry);
241 		if (!TAILQ_EMPTY(&c->tasks))
242 			conn_task_schedule(c);
243 		else
244 			session_schedule(c->session);
245 	}
246 }
247 
248 #define SET_NUM(p, x, v, min, max)				\
249 do {								\
250 	if (!strcmp((p)->key, #v)) {				\
251 		(x)->his.v = text_to_num((p)->value, (min), (max), &err); \
252 		if (err) {					\
253 			log_warnx("bad param %s=%s: %s",	\
254 			    (p)->key, (p)->value, err);		\
255 			errors++;				\
256 		}						\
257 log_debug("SET_NUM: %s = %llu", #v, (u_int64_t)(x)->his.v);	\
258 	}							\
259 } while (0)
260 
261 #define SET_BOOL(p, x, v)					\
262 do {								\
263 	if (!strcmp((p)->key, #v)) {				\
264 		(x)->his.v = text_to_bool((p)->value, &err);	\
265 		if (err) {					\
266 			log_warnx("bad param %s=%s: %s",	\
267 			    (p)->key, (p)->value, err);		\
268 			errors++;				\
269 		}						\
270 log_debug("SET_BOOL: %s = %u", #v, (int)(x)->his.v);		\
271 	}							\
272 } while (0)
273 
274 int
275 conn_parse_kvp(struct connection *c, struct kvp *kvp)
276 {
277 	struct kvp *k;
278 	struct session *s = c->session;
279 	const char *err;
280 	int errors = 0;
281 
282 
283 	for (k = kvp; k->key; k++) {
284 		SET_NUM(k, s, MaxBurstLength, 512, 16777215);
285 		SET_NUM(k, s, FirstBurstLength, 512, 16777215);
286 		SET_NUM(k, s, DefaultTime2Wait, 0, 3600);
287 		SET_NUM(k, s, DefaultTime2Retain, 0, 3600);
288 		SET_NUM(k, s, MaxOutstandingR2T, 1, 65535);
289 		SET_NUM(k, s, TargetPortalGroupTag, 1, 65535);
290 		SET_NUM(k, s, MaxConnections, 1, 65535);
291 		SET_BOOL(k, s, InitialR2T);
292 		SET_BOOL(k, s, ImmediateData);
293 		SET_BOOL(k, s, DataPDUInOrder);
294 		SET_BOOL(k, s, DataSequenceInOrder);
295 		SET_NUM(k, s, ErrorRecoveryLevel, 0, 2);
296 		SET_NUM(k, c, MaxRecvDataSegmentLength, 512, 16777215);
297 	}
298 
299 	if (errors) {
300 		log_warnx("conn_parse_kvp: errors found");
301 		return -1;
302 	}
303 	return 0;
304 }
305 
306 #undef SET_NUM
307 #undef SET_BOOL
308 
309 int
310 conn_gen_kvp(struct connection *c, struct kvp *kvp, size_t *nkvp)
311 {
312 	struct session *s = c->session;
313 	size_t i = 0;
314 
315 	if (s->mine.MaxConnections != iscsi_sess_defaults.MaxConnections) {
316 		i++;
317 		if (kvp && i < *nkvp) {
318 			kvp[i].key = strdup("MaxConnections");
319 			if (kvp[i].key == NULL)
320 				return (-1);
321 			if (asprintf(&kvp[i].value, "%u",
322 			    (unsigned int)s->mine.MaxConnections) == -1) {
323 				kvp[i].value = NULL;
324 				return (-1);
325 			}
326 		}
327 	}
328 	if (c->mine.MaxRecvDataSegmentLength !=
329 	    iscsi_conn_defaults.MaxRecvDataSegmentLength) {
330 		i++;
331 		if (kvp && i < *nkvp) {
332 			kvp[i].key = strdup("MaxRecvDataSegmentLength");
333 			if (kvp[i].key == NULL)
334 				return (-1);
335 			if (asprintf(&kvp[i].value, "%u",
336 			    (unsigned int)c->mine.MaxRecvDataSegmentLength) == -1) {
337 				kvp[i].value = NULL;
338 				return (-1);
339 			}
340 		}
341 	}
342 
343 	*nkvp = i;
344 	return (0);
345 }
346 
347 void
348 conn_pdu_write(struct connection *c, struct pdu *p)
349 {
350 	struct iscsi_pdu *ipdu;
351 
352 /* XXX I GUESS THIS SHOULD BE MOVED TO PDU SOMEHOW... */
353 	ipdu = pdu_getbuf(p, NULL, PDU_HEADER);
354 	switch (ISCSI_PDU_OPCODE(ipdu->opcode)) {
355 	case ISCSI_OP_I_NOP:
356 	case ISCSI_OP_SCSI_REQUEST:
357 	case ISCSI_OP_TASK_REQUEST:
358 	case ISCSI_OP_LOGIN_REQUEST:
359 	case ISCSI_OP_TEXT_REQUEST:
360 	case ISCSI_OP_DATA_OUT:
361 	case ISCSI_OP_LOGOUT_REQUEST:
362 	case ISCSI_OP_SNACK_REQUEST:
363 		ipdu->expstatsn = ntohl(c->expstatsn);
364 		break;
365 	}
366 
367 	TAILQ_INSERT_TAIL(&c->pdu_w, p, entry);
368 	event_add(&c->wev, NULL);
369 }
370 
371 /* connection state machine more or less as specified in the RFC */
372 struct {
373 	int		state;
374 	enum c_event	event;
375 	int		(*action)(struct connection *, enum c_event);
376 } fsm[] = {
377 	{ CONN_FREE, CONN_EV_CONNECT, c_do_connect },		/* T1 */
378 	{ CONN_XPT_WAIT, CONN_EV_CONNECTED, c_do_login },	/* T4 */
379 	{ CONN_IN_LOGIN, CONN_EV_LOGGED_IN, c_do_loggedin },	/* T5 */
380 	{ CONN_LOGGED_IN, CONN_EV_LOGOUT, c_do_logout },	/* T9 */
381 	{ CONN_LOGOUT_REQ, CONN_EV_LOGOUT, c_do_logout },	/* T10 */
382 	{ CONN_IN_LOGOUT, CONN_EV_LOGGED_OUT, c_do_loggedout },	/* T13 */
383 	{ CONN_ANYSTATE, CONN_EV_CLOSED, c_do_fail },
384 	{ CONN_ANYSTATE, CONN_EV_FAIL, c_do_fail },
385 	{ CONN_ANYSTATE, CONN_EV_FREE, c_do_fail },
386 	{ 0, 0, NULL }
387 };
388 
389 void
390 conn_fsm(struct connection *c, enum c_event event)
391 {
392 	int	i, ns;
393 
394 	for (i = 0; fsm[i].action != NULL; i++) {
395 		if (c->state & fsm[i].state && event == fsm[i].event) {
396 			log_debug("conn_fsm[%s]: %s ev %s",
397 			    c->session->config.SessionName,
398 			    conn_state(c->state), conn_event(event));
399 			ns = fsm[i].action(c, event);
400 			if (ns == -1)
401 				/* XXX better please */
402 				fatalx("conn_fsm: action failed");
403 			log_debug("conn_fsm[%s]: new state %s",
404 			    c->session->config.SessionName, conn_state(ns));
405 			c->state = ns;
406 			return;
407 		}
408 	}
409 	log_warnx("conn_fsm[%s]: unhandled state transition [%s, %s]",
410 	    c->session->config.SessionName, conn_state(c->state),
411 	    conn_event(event));
412 	fatalx("bork bork bork");
413 }
414 
415 int
416 c_do_connect(struct connection *c, enum c_event ev)
417 {
418 	if (c->fd == -1) {
419 		log_warnx("connect(%s), lost socket",
420 		    log_sockaddr(&c->config.TargetAddr));
421 		session_fsm(c->session, SESS_EV_CONN_FAIL, c);
422 		return (CONN_FREE);
423 	}
424 
425 	if (connect(c->fd, (struct sockaddr *)&c->config.TargetAddr,
426 	    c->config.TargetAddr.ss_len) == -1) {
427 		if (errno == EINPROGRESS) {
428 			event_add(&c->wev, NULL);
429 			return (CONN_XPT_WAIT);
430 		} else {
431 			log_warn("connect(%s)",
432 			    log_sockaddr(&c->config.TargetAddr));
433 			session_fsm(c->session, SESS_EV_CONN_FAIL, c);
434 			return (CONN_FREE);
435 		}
436 	}
437 	/* move forward */
438 	return (c_do_login(c, CONN_EV_CONNECTED));
439 }
440 
441 int
442 c_do_login(struct connection *c, enum c_event ev)
443 {
444 	/* start a login session and hope for the best ... */
445 	initiator_login(c);
446 	return (CONN_IN_LOGIN);
447 }
448 
449 int
450 c_do_loggedin(struct connection *c, enum c_event ev)
451 {
452 	session_fsm(c->session, SESS_EV_CONN_LOGGED_IN, c);
453 
454 	return (CONN_LOGGED_IN);
455 }
456 
457 int
458 c_do_logout(struct connection *c, enum c_event ev)
459 {
460 	/* logout is in progress ... */
461 	return (CONN_IN_LOGOUT);
462 }
463 
464 int
465 c_do_loggedout(struct connection *c, enum c_event ev)
466 {
467 	/* close TCP session and cleanup */
468 	event_del(&c->ev);
469 	event_del(&c->wev);
470 	close(c->fd);
471 
472 	/* session is informed by the logout handler */
473 	return (CONN_FREE);
474 }
475 
476 int
477 c_do_fail(struct connection *c, enum c_event ev)
478 {
479 	/* cleanup events so that the connection does not retrigger */
480 	event_del(&c->ev);
481 	event_del(&c->wev);
482 	close(c->fd);
483 
484 	session_fsm(c->session, SESS_EV_CONN_FAIL, c);
485 
486 	if (ev == CONN_EV_FREE || c->state & CONN_NEVER_LOGGED_IN)
487 		return (CONN_FREE);
488 	return (CONN_CLEANUP_WAIT);
489 }
490 
491 const char *
492 conn_state(int s)
493 {
494 	static char buf[15];
495 
496 	switch (s) {
497 	case CONN_FREE:
498 		return "FREE";
499 	case CONN_XPT_WAIT:
500 		return "XPT_WAIT";
501 	case CONN_XPT_UP:
502 		return "XPT_UP";
503 	case CONN_IN_LOGIN:
504 		return "IN_LOGIN";
505 	case CONN_LOGGED_IN:
506 		return "LOGGED_IN";
507 	case CONN_IN_LOGOUT:
508 		return "IN_LOGOUT";
509 	case CONN_LOGOUT_REQ:
510 		return "LOGOUT_REQ";
511 	case CONN_CLEANUP_WAIT:
512 		return "CLEANUP_WAIT";
513 	case CONN_IN_CLEANUP:
514 		return "IN_CLEANUP";
515 	default:
516 		snprintf(buf, sizeof(buf), "UKNWN %x", s);
517 		return buf;
518 	}
519 	/* NOTREACHED */
520 }
521 
522 const char *
523 conn_event(enum c_event e)
524 {
525 	static char buf[15];
526 
527 	switch (e) {
528 	case CONN_EV_FAIL:
529 		return "fail";
530 	case CONN_EV_CONNECT:
531 		return "connect";
532 	case CONN_EV_CONNECTED:
533 		return "connected";
534 	case CONN_EV_LOGGED_IN:
535 		return "logged in";
536 	case CONN_EV_LOGOUT:
537 		return "logout";
538 	case CONN_EV_LOGGED_OUT:
539 		return "logged out";
540 	case CONN_EV_CLOSED:
541 		return "closed";
542 	case CONN_EV_FREE:
543 		return "forced free";
544 	}
545 
546 	snprintf(buf, sizeof(buf), "UKNWN %d", e);
547 	return buf;
548 }
549