xref: /openbsd/usr.sbin/iscsid/connection.c (revision 2f43ab01)
1 /*	$OpenBSD: connection.c,v 1.21 2015/12/05 06:38:18 mmcc 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 <string.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_req_logout(struct connection *, enum c_event);
46 int	c_do_logout(struct connection *, enum c_event);
47 int	c_do_loggedout(struct connection *, enum c_event);
48 int	c_do_fail(struct connection *, enum c_event);
49 int	c_do_cleanup(struct connection *, enum c_event);
50 
51 const char *conn_state(int);
52 const char *conn_event(enum c_event);
53 
54 void
conn_new(struct session * s,struct connection_config * cc)55 conn_new(struct session *s, struct connection_config *cc)
56 {
57 	struct connection *c;
58 	int nodelay = 1;
59 
60 	if (!(c = calloc(1, sizeof(*c))))
61 		fatal("session_add_conn");
62 
63 	c->fd = -1;
64 	c->state = CONN_FREE;
65 	c->session = s;
66 	c->cid = arc4random();
67 	c->config = *cc;
68 	c->mine = initiator_conn_defaults;
69 	c->mine.HeaderDigest = s->config.HeaderDigest;
70 	c->mine.DataDigest = s->config.DataDigest;
71 	c->his = iscsi_conn_defaults;
72 	c->active = iscsi_conn_defaults;
73 
74 	TAILQ_INIT(&c->pdu_w);
75 	TAILQ_INIT(&c->tasks);
76 	TAILQ_INSERT_TAIL(&s->connections, c, entry);
77 
78 	if (pdu_readbuf_set(&c->prbuf, PDU_READ_SIZE)) {
79 		log_warn("conn_new");
80 		conn_free(c);
81 		return;
82 	}
83 
84 	/* create socket */
85 	c->fd = socket(c->config.TargetAddr.ss_family, SOCK_STREAM, 0);
86 	if (c->fd == -1) {
87 		log_warn("conn_new: socket");
88 		conn_free(c);
89 		return;
90 	}
91 	if (socket_setblockmode(c->fd, 1)) {
92 		log_warn("conn_new: socket_setblockmode");
93 		conn_free(c);
94 		return;
95 	}
96 
97 	/* try to turn off TCP Nagle */
98 	if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, &nodelay,
99 	    sizeof(nodelay)) == -1)
100 		log_warn("conn_new: setting TCP_NODELAY");
101 
102 	event_set(&c->ev, c->fd, EV_READ|EV_PERSIST, conn_dispatch, c);
103 	event_set(&c->wev, c->fd, EV_WRITE, conn_write_dispatch, c);
104 
105 	conn_fsm(c, CONN_EV_CONNECT);
106 }
107 
108 void
conn_free(struct connection * c)109 conn_free(struct connection *c)
110 {
111 	log_debug("conn_free");
112 
113 	pdu_readbuf_free(&c->prbuf);
114 	pdu_free_queue(&c->pdu_w);
115 
116 	event_del(&c->ev);
117 	event_del(&c->wev);
118 	if (c->fd != -1)
119 		close(c->fd);
120 
121 	taskq_cleanup(&c->tasks);
122 
123 	TAILQ_REMOVE(&c->session->connections, c, entry);
124 	free(c);
125 }
126 
127 void
conn_dispatch(int fd,short event,void * arg)128 conn_dispatch(int fd, short event, void *arg)
129 {
130 	struct connection *c = arg;
131 	ssize_t n;
132 
133 	if (!(event & EV_READ)) {
134 		log_debug("spurious read call");
135 		return;
136 	}
137 	if ((n = pdu_read(c)) == -1) {
138 		if (errno == EAGAIN || errno == ENOBUFS ||
139 		    errno == EINTR)	/* try later */
140 			return;
141 		log_warn("pdu_read");
142 		conn_fsm(c, CONN_EV_FAIL);
143 		return;
144 	}
145 	if (n == 0) {    /* connection closed */
146 		conn_fsm(c, CONN_EV_CLOSED);
147 		return;
148 	}
149 
150 	pdu_parse(c);
151 }
152 
153 void
conn_write_dispatch(int fd,short event,void * arg)154 conn_write_dispatch(int fd, short event, void *arg)
155 {
156 	struct connection *c = arg;
157 	ssize_t n;
158 	int error;
159 	socklen_t len;
160 
161 	if (!(event & EV_WRITE)) {
162 		log_debug("spurious write call");
163 		return;
164 	}
165 
166 	switch (c->state) {
167 	case CONN_XPT_WAIT:
168 		len = sizeof(error);
169 		if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR,
170 		    &error, &len) == -1 || (errno = error)) {
171 			log_warn("connect to %s failed",
172 			    log_sockaddr(&c->config.TargetAddr));
173 			conn_fsm(c, CONN_EV_FAIL);
174 			return;
175 		}
176 		conn_fsm(c, CONN_EV_CONNECTED);
177 		break;
178 	default:
179 		if ((n = pdu_write(c)) == -1) {
180 			log_warn("pdu_write");
181 			conn_fsm(c, CONN_EV_FAIL);
182 			return;
183 		}
184 		if (n == 0) {    /* connection closed */
185 			conn_fsm(c, CONN_EV_CLOSED);
186 			return;
187 		}
188 
189 		/* check if there is more to send */
190 		if (pdu_pending(c))
191 			event_add(&c->wev, NULL);
192 	}
193 }
194 
195 void
conn_fail(struct connection * c)196 conn_fail(struct connection *c)
197 {
198 	log_debug("conn_fail");
199 	conn_fsm(c, CONN_EV_FAIL);
200 }
201 
202 int
conn_task_ready(struct connection * c)203 conn_task_ready(struct connection *c)
204 {
205 	if ((c->state & CONN_RUNNING) && TAILQ_EMPTY(&c->tasks))
206 		return 1;
207 	return 0;
208 }
209 
210 void
conn_task_issue(struct connection * c,struct task * t)211 conn_task_issue(struct connection *c, struct task *t)
212 {
213 	TAILQ_INSERT_TAIL(&c->tasks, t, entry);
214 	conn_task_schedule(c);
215 }
216 
217 void
conn_task_schedule(struct connection * c)218 conn_task_schedule(struct connection *c)
219 {
220 	struct task *t = TAILQ_FIRST(&c->tasks);
221 	struct pdu *p, *np;
222 
223 	if (!t) {
224 		log_debug("conn_task_schedule: task is hiding");
225 		return;
226 	}
227 
228 	/* move pdus to the write queue */
229 	for (p = TAILQ_FIRST(&t->sendq); p != NULL; p = np) {
230 		np = TAILQ_NEXT(p, entry);
231 		TAILQ_REMOVE(&t->sendq, p, entry);
232 		conn_pdu_write(c, p);
233 	}
234 	if (t->callback == NULL) {
235 		/* no callback, immediate command expecting no answer */
236 		conn_task_cleanup(c, t);
237 		free(t);
238 	}
239 }
240 
241 void
conn_task_cleanup(struct connection * c,struct task * t)242 conn_task_cleanup(struct connection *c, struct task *t)
243 {
244 	pdu_free_queue(&t->sendq);
245 	pdu_free_queue(&t->recvq);
246 	/* XXX need some state to know if queued or not */
247 	if (c) {
248 		TAILQ_REMOVE(&c->tasks, t, entry);
249 		if (!TAILQ_EMPTY(&c->tasks))
250 			conn_task_schedule(c);
251 		else
252 			session_schedule(c->session);
253 	}
254 }
255 
256 #define SET_NUM(p, x, v, min, max)				\
257 do {								\
258 	if (!strcmp((p)->key, #v)) {				\
259 		(x)->his.v = text_to_num((p)->value, (min), (max), &err); \
260 		if (err) {					\
261 			log_warnx("bad param %s=%s: %s",	\
262 			    (p)->key, (p)->value, err);		\
263 			errors++;				\
264 		}						\
265 log_debug("SET_NUM: %s = %llu", #v, (u_int64_t)(x)->his.v);	\
266 	}							\
267 } while (0)
268 
269 #define SET_BOOL(p, x, v)					\
270 do {								\
271 	if (!strcmp((p)->key, #v)) {				\
272 		(x)->his.v = text_to_bool((p)->value, &err);	\
273 		if (err) {					\
274 			log_warnx("bad param %s=%s: %s",	\
275 			    (p)->key, (p)->value, err);		\
276 			errors++;				\
277 		}						\
278 log_debug("SET_BOOL: %s = %u", #v, (int)(x)->his.v);		\
279 	}							\
280 } while (0)
281 
282 int
conn_parse_kvp(struct connection * c,struct kvp * kvp)283 conn_parse_kvp(struct connection *c, struct kvp *kvp)
284 {
285 	struct kvp *k;
286 	struct session *s = c->session;
287 	const char *err;
288 	int errors = 0;
289 
290 
291 	for (k = kvp; k->key; k++) {
292 log_debug("conn_parse_kvp: %s = %s", k->key, k->value);
293 		/* XXX handle NotUnderstood|Irrelevant|Reject */
294 		SET_NUM(k, s, MaxBurstLength, 512, 16777215);
295 		SET_NUM(k, s, FirstBurstLength, 512, 16777215);
296 		SET_NUM(k, s, DefaultTime2Wait, 0, 3600);
297 		SET_NUM(k, s, DefaultTime2Retain, 0, 3600);
298 		SET_NUM(k, s, MaxOutstandingR2T, 1, 65535);
299 		SET_NUM(k, s, TargetPortalGroupTag, 0, 65535);
300 		SET_NUM(k, s, MaxConnections, 1, 65535);
301 		SET_BOOL(k, s, InitialR2T);
302 		SET_BOOL(k, s, ImmediateData);
303 		SET_BOOL(k, s, DataPDUInOrder);
304 		SET_BOOL(k, s, DataSequenceInOrder);
305 		SET_NUM(k, s, ErrorRecoveryLevel, 0, 2);
306 		SET_NUM(k, c, MaxRecvDataSegmentLength, 512, 16777215);
307 	}
308 
309 	if (errors) {
310 		log_warnx("conn_parse_kvp: errors found");
311 		return -1;
312 	}
313 	return 0;
314 }
315 
316 #undef SET_NUM
317 #undef SET_BOOL
318 
319 int
conn_gen_kvp(struct connection * c,struct kvp * kvp,size_t * nkvp)320 conn_gen_kvp(struct connection *c, struct kvp *kvp, size_t *nkvp)
321 {
322 	struct session *s = c->session;
323 	size_t i = 0;
324 
325 	if (s->mine.MaxConnections != iscsi_sess_defaults.MaxConnections) {
326 		if (kvp && i < *nkvp) {
327 			kvp[i].key = strdup("MaxConnections");
328 			if (kvp[i].key == NULL)
329 				return -1;
330 			if (asprintf(&kvp[i].value, "%hu",
331 			    s->mine.MaxConnections) == -1) {
332 				kvp[i].value = NULL;
333 				return -1;
334 			}
335 		}
336 		i++;
337 	}
338 	if (c->mine.MaxRecvDataSegmentLength !=
339 	    iscsi_conn_defaults.MaxRecvDataSegmentLength) {
340 		if (kvp && i < *nkvp) {
341 			kvp[i].key = strdup("MaxRecvDataSegmentLength");
342 			if (kvp[i].key == NULL)
343 				return -1;
344 			if (asprintf(&kvp[i].value, "%u",
345 			    c->mine.MaxRecvDataSegmentLength) == -1) {
346 				kvp[i].value = NULL;
347 				return -1;
348 			}
349 		}
350 		i++;
351 	}
352 
353 	*nkvp = i;
354 	return 0;
355 }
356 
357 void
conn_pdu_write(struct connection * c,struct pdu * p)358 conn_pdu_write(struct connection *c, struct pdu *p)
359 {
360 	struct iscsi_pdu *ipdu;
361 
362 /* XXX I GUESS THIS SHOULD BE MOVED TO PDU SOMEHOW... */
363 	ipdu = pdu_getbuf(p, NULL, PDU_HEADER);
364 	switch (ISCSI_PDU_OPCODE(ipdu->opcode)) {
365 	case ISCSI_OP_I_NOP:
366 	case ISCSI_OP_SCSI_REQUEST:
367 	case ISCSI_OP_TASK_REQUEST:
368 	case ISCSI_OP_LOGIN_REQUEST:
369 	case ISCSI_OP_TEXT_REQUEST:
370 	case ISCSI_OP_DATA_OUT:
371 	case ISCSI_OP_LOGOUT_REQUEST:
372 	case ISCSI_OP_SNACK_REQUEST:
373 		ipdu->expstatsn = ntohl(c->expstatsn);
374 		break;
375 	}
376 
377 	TAILQ_INSERT_TAIL(&c->pdu_w, p, entry);
378 	event_add(&c->wev, NULL);
379 }
380 
381 /* connection state machine more or less as specified in the RFC */
382 struct {
383 	int		state;
384 	enum c_event	event;
385 	int		(*action)(struct connection *, enum c_event);
386 } fsm[] = {
387 	{ CONN_FREE, CONN_EV_CONNECT, c_do_connect },		/* T1 */
388 	{ CONN_XPT_WAIT, CONN_EV_CONNECTED, c_do_login },	/* T4 */
389 	{ CONN_IN_LOGIN, CONN_EV_LOGGED_IN, c_do_loggedin },	/* T5 */
390 	{ CONN_LOGGED_IN, CONN_EV_LOGOUT, c_do_logout },	/* T9 */
391 	{ CONN_LOGGED_IN, CONN_EV_REQ_LOGOUT, c_do_req_logout },/* T11 */
392 	{ CONN_LOGOUT_REQ, CONN_EV_LOGOUT, c_do_logout },	/* T10 */
393 	{ CONN_LOGOUT_REQ, CONN_EV_REQ_LOGOUT, c_do_req_logout},/* T12 */
394 	{ CONN_LOGOUT_REQ, CONN_EV_LOGGED_OUT, c_do_loggedout },/* T18 */
395 	{ CONN_IN_LOGOUT, CONN_EV_LOGGED_OUT, c_do_loggedout },	/* T13 */
396 	{ CONN_IN_LOGOUT, CONN_EV_REQ_LOGOUT, c_do_req_logout },/* T14 */
397 	{ CONN_CLEANUP_WAIT, CONN_EV_CLEANING_UP, c_do_cleanup},/* M2 */
398 	{ CONN_CLEANUP_WAIT, CONN_EV_FREE, c_do_loggedout },	/* M1 */
399 	{ CONN_IN_CLEANUP, CONN_EV_FREE, c_do_loggedout },	/* M4 */
400 	{ CONN_IN_CLEANUP, CONN_EV_CLEANING_UP, c_do_cleanup},
401 	/* either one of T2, T7, T15, T16, T17, M3 */
402 	{ CONN_ANYSTATE, CONN_EV_CLOSED, c_do_fail },
403 	{ CONN_ANYSTATE, CONN_EV_FAIL, c_do_fail },
404 	{ CONN_ANYSTATE, CONN_EV_FREE, c_do_fail },
405 	{ 0, 0, NULL }
406 };
407 
408 void
conn_fsm(struct connection * c,enum c_event event)409 conn_fsm(struct connection *c, enum c_event event)
410 {
411 	int	i, ns;
412 
413 	for (i = 0; fsm[i].action != NULL; i++) {
414 		if (c->state & fsm[i].state && event == fsm[i].event) {
415 			log_debug("conn_fsm[%s]: %s ev %s",
416 			    c->session->config.SessionName,
417 			    conn_state(c->state), conn_event(event));
418 			ns = fsm[i].action(c, event);
419 			if (ns == -1)
420 				/* XXX better please */
421 				fatalx("conn_fsm: action failed");
422 			log_debug("conn_fsm[%s]: new state %s",
423 			    c->session->config.SessionName, conn_state(ns));
424 			c->state = ns;
425 			return;
426 		}
427 	}
428 	log_warnx("conn_fsm[%s]: unhandled state transition [%s, %s]",
429 	    c->session->config.SessionName, conn_state(c->state),
430 	    conn_event(event));
431 	fatalx("bork bork bork");
432 }
433 
434 int
c_do_connect(struct connection * c,enum c_event ev)435 c_do_connect(struct connection *c, enum c_event ev)
436 {
437 	if (c->fd == -1) {
438 		log_warnx("connect(%s), lost socket",
439 		    log_sockaddr(&c->config.TargetAddr));
440 		session_fsm(c->session, SESS_EV_CONN_FAIL, c, 0);
441 		return CONN_FREE;
442 	}
443 	if (c->config.LocalAddr.ss_len != 0) {
444 		if (bind(c->fd, (struct sockaddr *)&c->config.LocalAddr,
445 		    c->config.LocalAddr.ss_len) == -1) {
446 			log_warn("bind(%s)",
447 			    log_sockaddr(&c->config.LocalAddr));
448 			session_fsm(c->session, SESS_EV_CONN_FAIL, c, 0);
449 			return CONN_FREE;
450 		}
451 	}
452 	if (connect(c->fd, (struct sockaddr *)&c->config.TargetAddr,
453 	    c->config.TargetAddr.ss_len) == -1) {
454 		if (errno == EINPROGRESS) {
455 			event_add(&c->wev, NULL);
456 			event_add(&c->ev, NULL);
457 			return CONN_XPT_WAIT;
458 		} else {
459 			log_warn("connect(%s)",
460 			    log_sockaddr(&c->config.TargetAddr));
461 			session_fsm(c->session, SESS_EV_CONN_FAIL, c, 0);
462 			return CONN_FREE;
463 		}
464 	}
465 	event_add(&c->ev, NULL);
466 	/* move forward */
467 	return c_do_login(c, CONN_EV_CONNECTED);
468 }
469 
470 int
c_do_login(struct connection * c,enum c_event ev)471 c_do_login(struct connection *c, enum c_event ev)
472 {
473 	/* start a login session and hope for the best ... */
474 	initiator_login(c);
475 	return CONN_IN_LOGIN;
476 }
477 
478 int
c_do_loggedin(struct connection * c,enum c_event ev)479 c_do_loggedin(struct connection *c, enum c_event ev)
480 {
481 	iscsi_merge_conn_params(&c->active, &c->mine, &c->his);
482 	session_fsm(c->session, SESS_EV_CONN_LOGGED_IN, c, 0);
483 
484 	return CONN_LOGGED_IN;
485 }
486 
487 int
c_do_req_logout(struct connection * c,enum c_event ev)488 c_do_req_logout(struct connection *c, enum c_event ev)
489 {
490 	/* target requested logout. XXX implement async handler */
491 
492 	if (c->state & CONN_IN_LOGOUT)
493 		return CONN_IN_LOGOUT;
494 	else
495 		return CONN_LOGOUT_REQ;
496 }
497 
498 int
c_do_logout(struct connection * c,enum c_event ev)499 c_do_logout(struct connection *c, enum c_event ev)
500 {
501 	/* logout is in progress ... */
502 	return CONN_IN_LOGOUT;
503 }
504 
505 int
c_do_loggedout(struct connection * c,enum c_event ev)506 c_do_loggedout(struct connection *c, enum c_event ev)
507 {
508 	/*
509 	 * Called by the session fsm before calling conn_free.
510 	 * Doing this so the state transition is logged.
511 	 */
512 	return CONN_FREE;
513 }
514 
515 int
c_do_fail(struct connection * c,enum c_event ev)516 c_do_fail(struct connection *c, enum c_event ev)
517 {
518 	log_debug("c_do_fail");
519 
520 	/* cleanup events so that the connection does not retrigger */
521 	event_del(&c->ev);
522 	event_del(&c->wev);
523 	close(c->fd);
524 	c->fd = -1;	/* make sure this fd is not closed again */
525 
526 	/* all pending task have failed so clean them up */
527 	taskq_cleanup(&c->tasks);
528 
529 	/* session will take care of cleaning up the mess */
530 	session_fsm(c->session, SESS_EV_CONN_FAIL, c, 0);
531 
532 	if (ev == CONN_EV_FREE || c->state & CONN_NEVER_LOGGED_IN)
533 		return CONN_FREE;
534 	return CONN_CLEANUP_WAIT;
535 }
536 
537 int
c_do_cleanup(struct connection * c,enum c_event ev)538 c_do_cleanup(struct connection *c, enum c_event ev)
539 {
540 	/* nothing to do here just adjust state */
541 	return CONN_IN_CLEANUP;
542 }
543 
544 const char *
conn_state(int s)545 conn_state(int s)
546 {
547 	static char buf[15];
548 
549 	switch (s) {
550 	case CONN_FREE:
551 		return "FREE";
552 	case CONN_XPT_WAIT:
553 		return "XPT_WAIT";
554 	case CONN_XPT_UP:
555 		return "XPT_UP";
556 	case CONN_IN_LOGIN:
557 		return "IN_LOGIN";
558 	case CONN_LOGGED_IN:
559 		return "LOGGED_IN";
560 	case CONN_IN_LOGOUT:
561 		return "IN_LOGOUT";
562 	case CONN_LOGOUT_REQ:
563 		return "LOGOUT_REQ";
564 	case CONN_CLEANUP_WAIT:
565 		return "CLEANUP_WAIT";
566 	case CONN_IN_CLEANUP:
567 		return "IN_CLEANUP";
568 	default:
569 		snprintf(buf, sizeof(buf), "UKNWN %x", s);
570 		return buf;
571 	}
572 	/* NOTREACHED */
573 }
574 
575 const char *
conn_event(enum c_event e)576 conn_event(enum c_event e)
577 {
578 	static char buf[15];
579 
580 	switch (e) {
581 	case CONN_EV_FAIL:
582 		return "fail";
583 	case CONN_EV_CONNECT:
584 		return "connect";
585 	case CONN_EV_CONNECTED:
586 		return "connected";
587 	case CONN_EV_LOGGED_IN:
588 		return "logged in";
589 	case CONN_EV_REQ_LOGOUT:
590 		return "logout requested";
591 	case CONN_EV_LOGOUT:
592 		return "logout";
593 	case CONN_EV_LOGGED_OUT:
594 		return "logged out";
595 	case CONN_EV_CLEANING_UP:
596 		return "cleaning up";
597 	case CONN_EV_CLOSED:
598 		return "closed";
599 	case CONN_EV_FREE:
600 		return "forced free";
601 	}
602 
603 	snprintf(buf, sizeof(buf), "UKNWN %d", e);
604 	return buf;
605 }
606