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