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