GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/iscsid/connection.c Lines: 0 245 0.0 %
Date: 2017-11-07 Branches: 0 188 0.0 %

Line Branch Exec Source
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
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
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
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
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
196
conn_fail(struct connection *c)
197
{
198
	log_debug("conn_fail");
199
	conn_fsm(c, CONN_EV_FAIL);
200
}
201
202
int
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 *
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 *
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
}