1 /* $OpenBSD: connection.c,v 1.19 2014/05/10 11:30:47 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_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 /* XXX handle NotUnderstood|Irrelevant|Reject */ 293 SET_NUM(k, s, MaxBurstLength, 512, 16777215); 294 SET_NUM(k, s, FirstBurstLength, 512, 16777215); 295 SET_NUM(k, s, DefaultTime2Wait, 0, 3600); 296 SET_NUM(k, s, DefaultTime2Retain, 0, 3600); 297 SET_NUM(k, s, MaxOutstandingR2T, 1, 65535); 298 SET_NUM(k, s, TargetPortalGroupTag, 1, 65535); 299 SET_NUM(k, s, MaxConnections, 1, 65535); 300 SET_BOOL(k, s, InitialR2T); 301 SET_BOOL(k, s, ImmediateData); 302 SET_BOOL(k, s, DataPDUInOrder); 303 SET_BOOL(k, s, DataSequenceInOrder); 304 SET_NUM(k, s, ErrorRecoveryLevel, 0, 2); 305 SET_NUM(k, c, MaxRecvDataSegmentLength, 512, 16777215); 306 } 307 308 if (errors) { 309 log_warnx("conn_parse_kvp: errors found"); 310 return -1; 311 } 312 return 0; 313 } 314 315 #undef SET_NUM 316 #undef SET_BOOL 317 318 int 319 conn_gen_kvp(struct connection *c, struct kvp *kvp, size_t *nkvp) 320 { 321 struct session *s = c->session; 322 size_t i = 0; 323 324 if (s->mine.MaxConnections != iscsi_sess_defaults.MaxConnections) { 325 if (kvp && i < *nkvp) { 326 kvp[i].key = strdup("MaxConnections"); 327 if (kvp[i].key == NULL) 328 return -1; 329 if (asprintf(&kvp[i].value, "%hu", 330 s->mine.MaxConnections) == -1) { 331 kvp[i].value = NULL; 332 return -1; 333 } 334 } 335 i++; 336 } 337 if (c->mine.MaxRecvDataSegmentLength != 338 iscsi_conn_defaults.MaxRecvDataSegmentLength) { 339 if (kvp && i < *nkvp) { 340 kvp[i].key = strdup("MaxRecvDataSegmentLength"); 341 if (kvp[i].key == NULL) 342 return -1; 343 if (asprintf(&kvp[i].value, "%u", 344 c->mine.MaxRecvDataSegmentLength) == -1) { 345 kvp[i].value = NULL; 346 return -1; 347 } 348 } 349 i++; 350 } 351 352 *nkvp = i; 353 return 0; 354 } 355 356 void 357 conn_pdu_write(struct connection *c, struct pdu *p) 358 { 359 struct iscsi_pdu *ipdu; 360 361 /* XXX I GUESS THIS SHOULD BE MOVED TO PDU SOMEHOW... */ 362 ipdu = pdu_getbuf(p, NULL, PDU_HEADER); 363 switch (ISCSI_PDU_OPCODE(ipdu->opcode)) { 364 case ISCSI_OP_I_NOP: 365 case ISCSI_OP_SCSI_REQUEST: 366 case ISCSI_OP_TASK_REQUEST: 367 case ISCSI_OP_LOGIN_REQUEST: 368 case ISCSI_OP_TEXT_REQUEST: 369 case ISCSI_OP_DATA_OUT: 370 case ISCSI_OP_LOGOUT_REQUEST: 371 case ISCSI_OP_SNACK_REQUEST: 372 ipdu->expstatsn = ntohl(c->expstatsn); 373 break; 374 } 375 376 TAILQ_INSERT_TAIL(&c->pdu_w, p, entry); 377 event_add(&c->wev, NULL); 378 } 379 380 /* connection state machine more or less as specified in the RFC */ 381 struct { 382 int state; 383 enum c_event event; 384 int (*action)(struct connection *, enum c_event); 385 } fsm[] = { 386 { CONN_FREE, CONN_EV_CONNECT, c_do_connect }, /* T1 */ 387 { CONN_XPT_WAIT, CONN_EV_CONNECTED, c_do_login }, /* T4 */ 388 { CONN_IN_LOGIN, CONN_EV_LOGGED_IN, c_do_loggedin }, /* T5 */ 389 { CONN_LOGGED_IN, CONN_EV_LOGOUT, c_do_logout }, /* T9 */ 390 { CONN_LOGGED_IN, CONN_EV_REQ_LOGOUT, c_do_req_logout },/* T11 */ 391 { CONN_LOGOUT_REQ, CONN_EV_LOGOUT, c_do_logout }, /* T10 */ 392 { CONN_LOGOUT_REQ, CONN_EV_REQ_LOGOUT, c_do_req_logout},/* T12 */ 393 { CONN_LOGOUT_REQ, CONN_EV_LOGGED_OUT, c_do_loggedout },/* T18 */ 394 { CONN_IN_LOGOUT, CONN_EV_LOGGED_OUT, c_do_loggedout }, /* T13 */ 395 { CONN_IN_LOGOUT, CONN_EV_REQ_LOGOUT, c_do_req_logout },/* T14 */ 396 { CONN_CLEANUP_WAIT, CONN_EV_CLEANING_UP, c_do_cleanup},/* M2 */ 397 { CONN_CLEANUP_WAIT, CONN_EV_FREE, c_do_loggedout }, /* M1 */ 398 { CONN_IN_CLEANUP, CONN_EV_FREE, c_do_loggedout }, /* M4 */ 399 { CONN_IN_CLEANUP, CONN_EV_CLEANING_UP, c_do_cleanup}, 400 /* either one of T2, T7, T15, T16, T17, M3 */ 401 { CONN_ANYSTATE, CONN_EV_CLOSED, c_do_fail }, 402 { CONN_ANYSTATE, CONN_EV_FAIL, c_do_fail }, 403 { CONN_ANYSTATE, CONN_EV_FREE, c_do_fail }, 404 { 0, 0, NULL } 405 }; 406 407 void 408 conn_fsm(struct connection *c, enum c_event event) 409 { 410 int i, ns; 411 412 for (i = 0; fsm[i].action != NULL; i++) { 413 if (c->state & fsm[i].state && event == fsm[i].event) { 414 log_debug("conn_fsm[%s]: %s ev %s", 415 c->session->config.SessionName, 416 conn_state(c->state), conn_event(event)); 417 ns = fsm[i].action(c, event); 418 if (ns == -1) 419 /* XXX better please */ 420 fatalx("conn_fsm: action failed"); 421 log_debug("conn_fsm[%s]: new state %s", 422 c->session->config.SessionName, conn_state(ns)); 423 c->state = ns; 424 return; 425 } 426 } 427 log_warnx("conn_fsm[%s]: unhandled state transition [%s, %s]", 428 c->session->config.SessionName, conn_state(c->state), 429 conn_event(event)); 430 fatalx("bork bork bork"); 431 } 432 433 int 434 c_do_connect(struct connection *c, enum c_event ev) 435 { 436 if (c->fd == -1) { 437 log_warnx("connect(%s), lost socket", 438 log_sockaddr(&c->config.TargetAddr)); 439 session_fsm(c->session, SESS_EV_CONN_FAIL, c, 0); 440 return CONN_FREE; 441 } 442 if (c->config.LocalAddr.ss_len != 0) { 443 if (bind(c->fd, (struct sockaddr *)&c->config.LocalAddr, 444 c->config.LocalAddr.ss_len) == -1) { 445 log_warn("bind(%s)", 446 log_sockaddr(&c->config.LocalAddr)); 447 session_fsm(c->session, SESS_EV_CONN_FAIL, c, 0); 448 return CONN_FREE; 449 } 450 } 451 if (connect(c->fd, (struct sockaddr *)&c->config.TargetAddr, 452 c->config.TargetAddr.ss_len) == -1) { 453 if (errno == EINPROGRESS) { 454 event_add(&c->wev, NULL); 455 event_add(&c->ev, NULL); 456 return CONN_XPT_WAIT; 457 } else { 458 log_warn("connect(%s)", 459 log_sockaddr(&c->config.TargetAddr)); 460 session_fsm(c->session, SESS_EV_CONN_FAIL, c, 0); 461 return CONN_FREE; 462 } 463 } 464 event_add(&c->ev, NULL); 465 /* move forward */ 466 return c_do_login(c, CONN_EV_CONNECTED); 467 } 468 469 int 470 c_do_login(struct connection *c, enum c_event ev) 471 { 472 /* start a login session and hope for the best ... */ 473 initiator_login(c); 474 return CONN_IN_LOGIN; 475 } 476 477 int 478 c_do_loggedin(struct connection *c, enum c_event ev) 479 { 480 iscsi_merge_conn_params(&c->active, &c->mine, &c->his); 481 session_fsm(c->session, SESS_EV_CONN_LOGGED_IN, c, 0); 482 483 return CONN_LOGGED_IN; 484 } 485 486 int 487 c_do_req_logout(struct connection *c, enum c_event ev) 488 { 489 /* target requested logout. XXX implement async handler */ 490 491 if (c->state & CONN_IN_LOGOUT) 492 return CONN_IN_LOGOUT; 493 else 494 return CONN_LOGOUT_REQ; 495 } 496 497 int 498 c_do_logout(struct connection *c, enum c_event ev) 499 { 500 /* logout is in progress ... */ 501 return CONN_IN_LOGOUT; 502 } 503 504 int 505 c_do_loggedout(struct connection *c, enum c_event ev) 506 { 507 /* 508 * Called by the session fsm before calling conn_free. 509 * Doing this so the state transition is logged. 510 */ 511 return CONN_FREE; 512 } 513 514 int 515 c_do_fail(struct connection *c, enum c_event ev) 516 { 517 log_debug("c_do_fail"); 518 519 /* cleanup events so that the connection does not retrigger */ 520 event_del(&c->ev); 521 event_del(&c->wev); 522 close(c->fd); 523 c->fd = -1; /* make sure this fd is not closed again */ 524 525 /* all pending task have failed so clean them up */ 526 taskq_cleanup(&c->tasks); 527 528 /* session will take care of cleaning up the mess */ 529 session_fsm(c->session, SESS_EV_CONN_FAIL, c, 0); 530 531 if (ev == CONN_EV_FREE || c->state & CONN_NEVER_LOGGED_IN) 532 return CONN_FREE; 533 return CONN_CLEANUP_WAIT; 534 } 535 536 int 537 c_do_cleanup(struct connection *c, enum c_event ev) 538 { 539 /* nothing to do here just adjust state */ 540 return CONN_IN_CLEANUP; 541 } 542 543 const char * 544 conn_state(int s) 545 { 546 static char buf[15]; 547 548 switch (s) { 549 case CONN_FREE: 550 return "FREE"; 551 case CONN_XPT_WAIT: 552 return "XPT_WAIT"; 553 case CONN_XPT_UP: 554 return "XPT_UP"; 555 case CONN_IN_LOGIN: 556 return "IN_LOGIN"; 557 case CONN_LOGGED_IN: 558 return "LOGGED_IN"; 559 case CONN_IN_LOGOUT: 560 return "IN_LOGOUT"; 561 case CONN_LOGOUT_REQ: 562 return "LOGOUT_REQ"; 563 case CONN_CLEANUP_WAIT: 564 return "CLEANUP_WAIT"; 565 case CONN_IN_CLEANUP: 566 return "IN_CLEANUP"; 567 default: 568 snprintf(buf, sizeof(buf), "UKNWN %x", s); 569 return buf; 570 } 571 /* NOTREACHED */ 572 } 573 574 const char * 575 conn_event(enum c_event e) 576 { 577 static char buf[15]; 578 579 switch (e) { 580 case CONN_EV_FAIL: 581 return "fail"; 582 case CONN_EV_CONNECT: 583 return "connect"; 584 case CONN_EV_CONNECTED: 585 return "connected"; 586 case CONN_EV_LOGGED_IN: 587 return "logged in"; 588 case CONN_EV_REQ_LOGOUT: 589 return "logout requested"; 590 case CONN_EV_LOGOUT: 591 return "logout"; 592 case CONN_EV_LOGGED_OUT: 593 return "logged out"; 594 case CONN_EV_CLEANING_UP: 595 return "cleaning up"; 596 case CONN_EV_CLOSED: 597 return "closed"; 598 case CONN_EV_FREE: 599 return "forced free"; 600 } 601 602 snprintf(buf, sizeof(buf), "UKNWN %d", e); 603 return buf; 604 } 605