xref: /openbsd/usr.sbin/iscsid/connection.c (revision 4b539f84)
1 /*	$OpenBSD: connection.c,v 1.18 2014/04/21 18:59:05 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 		if (errno == EAGAIN || errno == ENOBUFS ||
135 		    errno == EINTR)	/* try later */
136 			return;
137 		log_warn("pdu_read");
138 		conn_fsm(c, CONN_EV_FAIL);
139 		return;
140 	}
141 	if (n == 0) {    /* connection closed */
142 		conn_fsm(c, CONN_EV_CLOSED);
143 		return;
144 	}
145 
146 	pdu_parse(c);
147 }
148 
149 void
150 conn_write_dispatch(int fd, short event, void *arg)
151 {
152 	struct connection *c = arg;
153 	ssize_t n;
154 	int error;
155 	socklen_t len;
156 
157 	if (!(event & EV_WRITE)) {
158 		log_debug("spurious write call");
159 		return;
160 	}
161 
162 	switch (c->state) {
163 	case CONN_XPT_WAIT:
164 		len = sizeof(error);
165 		if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR,
166 		    &error, &len) == -1 || (errno = error)) {
167 			log_warn("cwd connect(%s)",
168 			    log_sockaddr(&c->config.TargetAddr));
169 			conn_fsm(c, CONN_EV_FAIL);
170 			return;
171 		}
172 		conn_fsm(c, CONN_EV_CONNECTED);
173 		break;
174 	default:
175 		if ((n = pdu_write(c)) == -1) {
176 			log_warn("pdu_write");
177 			conn_fsm(c, CONN_EV_FAIL);
178 			return;
179 		}
180 		if (n == 0) {    /* connection closed */
181 			conn_fsm(c, CONN_EV_CLOSED);
182 			return;
183 		}
184 
185 		/* check if there is more to send */
186 		if (pdu_pending(c))
187 			event_add(&c->wev, NULL);
188 	}
189 }
190 
191 void
192 conn_fail(struct connection *c)
193 {
194 	log_debug("conn_fail");
195 	conn_fsm(c, CONN_EV_FAIL);
196 }
197 
198 int
199 conn_task_ready(struct connection *c)
200 {
201 	if ((c->state & CONN_RUNNING) && TAILQ_EMPTY(&c->tasks))
202 		return 1;
203 	return 0;
204 }
205 
206 void
207 conn_task_issue(struct connection *c, struct task *t)
208 {
209 	TAILQ_INSERT_TAIL(&c->tasks, t, entry);
210 	conn_task_schedule(c);
211 }
212 
213 void
214 conn_task_schedule(struct connection *c)
215 {
216 	struct task *t = TAILQ_FIRST(&c->tasks);
217 	struct pdu *p, *np;
218 
219 	if (!t) {
220 		log_debug("conn_task_schedule: task is hiding");
221 		return;
222 	}
223 
224 	/* move pdus to the write queue */
225 	for (p = TAILQ_FIRST(&t->sendq); p != NULL; p = np) {
226 		np = TAILQ_NEXT(p, entry);
227 		TAILQ_REMOVE(&t->sendq, p, entry);
228 		conn_pdu_write(c, p);
229 	}
230 	if (t->callback == NULL) {
231 		/* no callback, immediate command expecting no answer */
232 		conn_task_cleanup(c, t);
233 		free(t);
234 	}
235 }
236 
237 void
238 conn_task_cleanup(struct connection *c, struct task *t)
239 {
240 	pdu_free_queue(&t->sendq);
241 	pdu_free_queue(&t->recvq);
242 	/* XXX need some state to know if queued or not */
243 	if (c) {
244 		TAILQ_REMOVE(&c->tasks, t, entry);
245 		if (!TAILQ_EMPTY(&c->tasks))
246 			conn_task_schedule(c);
247 		else
248 			session_schedule(c->session);
249 	}
250 }
251 
252 #define SET_NUM(p, x, v, min, max)				\
253 do {								\
254 	if (!strcmp((p)->key, #v)) {				\
255 		(x)->his.v = text_to_num((p)->value, (min), (max), &err); \
256 		if (err) {					\
257 			log_warnx("bad param %s=%s: %s",	\
258 			    (p)->key, (p)->value, err);		\
259 			errors++;				\
260 		}						\
261 log_debug("SET_NUM: %s = %llu", #v, (u_int64_t)(x)->his.v);	\
262 	}							\
263 } while (0)
264 
265 #define SET_BOOL(p, x, v)					\
266 do {								\
267 	if (!strcmp((p)->key, #v)) {				\
268 		(x)->his.v = text_to_bool((p)->value, &err);	\
269 		if (err) {					\
270 			log_warnx("bad param %s=%s: %s",	\
271 			    (p)->key, (p)->value, err);		\
272 			errors++;				\
273 		}						\
274 log_debug("SET_BOOL: %s = %u", #v, (int)(x)->his.v);		\
275 	}							\
276 } while (0)
277 
278 int
279 conn_parse_kvp(struct connection *c, struct kvp *kvp)
280 {
281 	struct kvp *k;
282 	struct session *s = c->session;
283 	const char *err;
284 	int errors = 0;
285 
286 
287 	for (k = kvp; k->key; k++) {
288 		/* XXX handle NotUnderstood|Irrelevant|Reject */
289 		SET_NUM(k, s, MaxBurstLength, 512, 16777215);
290 		SET_NUM(k, s, FirstBurstLength, 512, 16777215);
291 		SET_NUM(k, s, DefaultTime2Wait, 0, 3600);
292 		SET_NUM(k, s, DefaultTime2Retain, 0, 3600);
293 		SET_NUM(k, s, MaxOutstandingR2T, 1, 65535);
294 		SET_NUM(k, s, TargetPortalGroupTag, 1, 65535);
295 		SET_NUM(k, s, MaxConnections, 1, 65535);
296 		SET_BOOL(k, s, InitialR2T);
297 		SET_BOOL(k, s, ImmediateData);
298 		SET_BOOL(k, s, DataPDUInOrder);
299 		SET_BOOL(k, s, DataSequenceInOrder);
300 		SET_NUM(k, s, ErrorRecoveryLevel, 0, 2);
301 		SET_NUM(k, c, MaxRecvDataSegmentLength, 512, 16777215);
302 	}
303 
304 	if (errors) {
305 		log_warnx("conn_parse_kvp: errors found");
306 		return -1;
307 	}
308 	return 0;
309 }
310 
311 #undef SET_NUM
312 #undef SET_BOOL
313 
314 int
315 conn_gen_kvp(struct connection *c, struct kvp *kvp, size_t *nkvp)
316 {
317 	struct session *s = c->session;
318 	size_t i = 0;
319 
320 	if (s->mine.MaxConnections != iscsi_sess_defaults.MaxConnections) {
321 		if (kvp && i < *nkvp) {
322 			kvp[i].key = strdup("MaxConnections");
323 			if (kvp[i].key == NULL)
324 				return -1;
325 			if (asprintf(&kvp[i].value, "%hu",
326 			    s->mine.MaxConnections) == -1) {
327 				kvp[i].value = NULL;
328 				return -1;
329 			}
330 		}
331 		i++;
332 	}
333 	if (c->mine.MaxRecvDataSegmentLength !=
334 	    iscsi_conn_defaults.MaxRecvDataSegmentLength) {
335 		if (kvp && i < *nkvp) {
336 			kvp[i].key = strdup("MaxRecvDataSegmentLength");
337 			if (kvp[i].key == NULL)
338 				return -1;
339 			if (asprintf(&kvp[i].value, "%u",
340 			    c->mine.MaxRecvDataSegmentLength) == -1) {
341 				kvp[i].value = NULL;
342 				return -1;
343 			}
344 		}
345 		i++;
346 	}
347 
348 	*nkvp = i;
349 	return 0;
350 }
351 
352 void
353 conn_pdu_write(struct connection *c, struct pdu *p)
354 {
355 	struct iscsi_pdu *ipdu;
356 
357 /* XXX I GUESS THIS SHOULD BE MOVED TO PDU SOMEHOW... */
358 	ipdu = pdu_getbuf(p, NULL, PDU_HEADER);
359 	switch (ISCSI_PDU_OPCODE(ipdu->opcode)) {
360 	case ISCSI_OP_I_NOP:
361 	case ISCSI_OP_SCSI_REQUEST:
362 	case ISCSI_OP_TASK_REQUEST:
363 	case ISCSI_OP_LOGIN_REQUEST:
364 	case ISCSI_OP_TEXT_REQUEST:
365 	case ISCSI_OP_DATA_OUT:
366 	case ISCSI_OP_LOGOUT_REQUEST:
367 	case ISCSI_OP_SNACK_REQUEST:
368 		ipdu->expstatsn = ntohl(c->expstatsn);
369 		break;
370 	}
371 
372 	TAILQ_INSERT_TAIL(&c->pdu_w, p, entry);
373 	event_add(&c->wev, NULL);
374 }
375 
376 /* connection state machine more or less as specified in the RFC */
377 struct {
378 	int		state;
379 	enum c_event	event;
380 	int		(*action)(struct connection *, enum c_event);
381 } fsm[] = {
382 	{ CONN_FREE, CONN_EV_CONNECT, c_do_connect },		/* T1 */
383 	{ CONN_XPT_WAIT, CONN_EV_CONNECTED, c_do_login },	/* T4 */
384 	{ CONN_IN_LOGIN, CONN_EV_LOGGED_IN, c_do_loggedin },	/* T5 */
385 	{ CONN_LOGGED_IN, CONN_EV_LOGOUT, c_do_logout },	/* T9 */
386 	{ CONN_LOGOUT_REQ, CONN_EV_LOGOUT, c_do_logout },	/* T10 */
387 	{ CONN_IN_LOGOUT, CONN_EV_LOGGED_OUT, c_do_loggedout },	/* T13 */
388 	{ CONN_ANYSTATE, CONN_EV_CLOSED, c_do_fail },
389 	{ CONN_ANYSTATE, CONN_EV_FAIL, c_do_fail },
390 	{ CONN_ANYSTATE, CONN_EV_FREE, c_do_fail },
391 	{ 0, 0, NULL }
392 };
393 
394 void
395 conn_fsm(struct connection *c, enum c_event event)
396 {
397 	int	i, ns;
398 
399 	for (i = 0; fsm[i].action != NULL; i++) {
400 		if (c->state & fsm[i].state && event == fsm[i].event) {
401 			log_debug("conn_fsm[%s]: %s ev %s",
402 			    c->session->config.SessionName,
403 			    conn_state(c->state), conn_event(event));
404 			ns = fsm[i].action(c, event);
405 			if (ns == -1)
406 				/* XXX better please */
407 				fatalx("conn_fsm: action failed");
408 			log_debug("conn_fsm[%s]: new state %s",
409 			    c->session->config.SessionName, conn_state(ns));
410 			c->state = ns;
411 			return;
412 		}
413 	}
414 	log_warnx("conn_fsm[%s]: unhandled state transition [%s, %s]",
415 	    c->session->config.SessionName, conn_state(c->state),
416 	    conn_event(event));
417 	fatalx("bork bork bork");
418 }
419 
420 int
421 c_do_connect(struct connection *c, enum c_event ev)
422 {
423 	if (c->fd == -1) {
424 		log_warnx("connect(%s), lost socket",
425 		    log_sockaddr(&c->config.TargetAddr));
426 		session_fsm(c->session, SESS_EV_CONN_FAIL, c);
427 		return CONN_FREE;
428 	}
429 	if (c->config.LocalAddr.ss_len != 0) {
430 		if (bind(c->fd, (struct sockaddr *)&c->config.LocalAddr,
431 		    c->config.LocalAddr.ss_len) == -1) {
432 			log_warn("bind(%s)",
433 			    log_sockaddr(&c->config.LocalAddr));
434 			session_fsm(c->session, SESS_EV_CONN_FAIL, c);
435 			return CONN_FREE;
436 		}
437 	}
438 	if (connect(c->fd, (struct sockaddr *)&c->config.TargetAddr,
439 	    c->config.TargetAddr.ss_len) == -1) {
440 		if (errno == EINPROGRESS) {
441 			event_add(&c->wev, NULL);
442 			return CONN_XPT_WAIT;
443 		} else {
444 			log_warn("connect(%s)",
445 			    log_sockaddr(&c->config.TargetAddr));
446 			session_fsm(c->session, SESS_EV_CONN_FAIL, c);
447 			return CONN_FREE;
448 		}
449 	}
450 	/* move forward */
451 	return c_do_login(c, CONN_EV_CONNECTED);
452 }
453 
454 int
455 c_do_login(struct connection *c, enum c_event ev)
456 {
457 	/* start a login session and hope for the best ... */
458 	initiator_login(c);
459 	return CONN_IN_LOGIN;
460 }
461 
462 int
463 c_do_loggedin(struct connection *c, enum c_event ev)
464 {
465 	iscsi_merge_conn_params(&c->active, &c->mine, &c->his);
466 	session_fsm(c->session, SESS_EV_CONN_LOGGED_IN, c);
467 
468 	return CONN_LOGGED_IN;
469 }
470 
471 int
472 c_do_logout(struct connection *c, enum c_event ev)
473 {
474 	/* logout is in progress ... */
475 	return CONN_IN_LOGOUT;
476 }
477 
478 int
479 c_do_loggedout(struct connection *c, enum c_event ev)
480 {
481 	/* close TCP session and cleanup */
482 	event_del(&c->ev);
483 	event_del(&c->wev);
484 	close(c->fd);
485 
486 	/* session is informed by the logout handler */
487 	return CONN_FREE;
488 }
489 
490 int
491 c_do_fail(struct connection *c, enum c_event ev)
492 {
493 	/* cleanup events so that the connection does not retrigger */
494 	event_del(&c->ev);
495 	event_del(&c->wev);
496 	close(c->fd);
497 
498 	session_fsm(c->session, SESS_EV_CONN_FAIL, c);
499 
500 	if (ev == CONN_EV_FREE || c->state & CONN_NEVER_LOGGED_IN)
501 		return CONN_FREE;
502 	return CONN_CLEANUP_WAIT;
503 }
504 
505 const char *
506 conn_state(int s)
507 {
508 	static char buf[15];
509 
510 	switch (s) {
511 	case CONN_FREE:
512 		return "FREE";
513 	case CONN_XPT_WAIT:
514 		return "XPT_WAIT";
515 	case CONN_XPT_UP:
516 		return "XPT_UP";
517 	case CONN_IN_LOGIN:
518 		return "IN_LOGIN";
519 	case CONN_LOGGED_IN:
520 		return "LOGGED_IN";
521 	case CONN_IN_LOGOUT:
522 		return "IN_LOGOUT";
523 	case CONN_LOGOUT_REQ:
524 		return "LOGOUT_REQ";
525 	case CONN_CLEANUP_WAIT:
526 		return "CLEANUP_WAIT";
527 	case CONN_IN_CLEANUP:
528 		return "IN_CLEANUP";
529 	default:
530 		snprintf(buf, sizeof(buf), "UKNWN %x", s);
531 		return buf;
532 	}
533 	/* NOTREACHED */
534 }
535 
536 const char *
537 conn_event(enum c_event e)
538 {
539 	static char buf[15];
540 
541 	switch (e) {
542 	case CONN_EV_FAIL:
543 		return "fail";
544 	case CONN_EV_CONNECT:
545 		return "connect";
546 	case CONN_EV_CONNECTED:
547 		return "connected";
548 	case CONN_EV_LOGGED_IN:
549 		return "logged in";
550 	case CONN_EV_LOGOUT:
551 		return "logout";
552 	case CONN_EV_LOGGED_OUT:
553 		return "logged out";
554 	case CONN_EV_CLOSED:
555 		return "closed";
556 	case CONN_EV_FREE:
557 		return "forced free";
558 	}
559 
560 	snprintf(buf, sizeof(buf), "UKNWN %d", e);
561 	return buf;
562 }
563