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 } 606