1 /* $OpenBSD: connection.c,v 1.13 2011/05/04 21:00:04 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 conn_fsm(c, CONN_EV_FAIL); 135 return; 136 } 137 if (n == 0) { /* connection closed */ 138 conn_fsm(c, CONN_EV_CLOSED); 139 return; 140 } 141 142 pdu_parse(c); 143 } 144 145 void 146 conn_write_dispatch(int fd, short event, void *arg) 147 { 148 struct connection *c = arg; 149 ssize_t n; 150 int error; 151 socklen_t len; 152 153 if (!(event & EV_WRITE)) { 154 log_debug("spurious write call"); 155 return; 156 } 157 158 switch (c->state) { 159 case CONN_XPT_WAIT: 160 len = sizeof(error); 161 if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, 162 &error, &len) == -1 || (errno = error)) { 163 log_warn("cwd connect(%s)", 164 log_sockaddr(&c->config.TargetAddr)); 165 conn_fsm(c, CONN_EV_FAIL); 166 return; 167 } 168 conn_fsm(c, CONN_EV_CONNECTED); 169 break; 170 default: 171 if ((n = pdu_write(c)) == -1) { 172 log_warn("pdu_write"); 173 conn_fsm(c, CONN_EV_FAIL); 174 return; 175 } 176 if (n == 0) { /* connection closed */ 177 conn_fsm(c, CONN_EV_CLOSED); 178 return; 179 } 180 181 /* check if there is more to send */ 182 if (pdu_pending(c)) 183 event_add(&c->wev, NULL); 184 } 185 } 186 187 void 188 conn_fail(struct connection *c) 189 { 190 log_debug("conn_fail"); 191 conn_fsm(c, CONN_EV_FAIL); 192 } 193 194 int 195 conn_task_ready(struct connection *c) 196 { 197 if ((c->state & CONN_RUNNING) && TAILQ_EMPTY(&c->tasks)) 198 return 1; 199 return 0; 200 } 201 202 void 203 conn_task_issue(struct connection *c, struct task *t) 204 { 205 TAILQ_INSERT_TAIL(&c->tasks, t, entry); 206 conn_task_schedule(c); 207 } 208 209 void 210 conn_task_schedule(struct connection *c) 211 { 212 struct task *t = TAILQ_FIRST(&c->tasks); 213 struct pdu *p, *np; 214 215 if (!t) { 216 log_debug("conn_task_schedule: task is hiding"); 217 return; 218 } 219 220 /* move pdus to the write queue */ 221 for (p = TAILQ_FIRST(&t->sendq); p != NULL; p = np) { 222 np = TAILQ_NEXT(p, entry); 223 TAILQ_REMOVE(&t->sendq, p, entry); 224 conn_pdu_write(c, p); 225 } 226 if (t->callback == NULL) { 227 /* no callback, immediate command expecting no answer */ 228 conn_task_cleanup(c, t); 229 free(t); 230 } 231 } 232 233 void 234 conn_task_cleanup(struct connection *c, struct task *t) 235 { 236 pdu_free_queue(&t->sendq); 237 pdu_free_queue(&t->recvq); 238 /* XXX need some state to know if queued or not */ 239 if (c) { 240 TAILQ_REMOVE(&c->tasks, t, entry); 241 if (!TAILQ_EMPTY(&c->tasks)) 242 conn_task_schedule(c); 243 else 244 session_schedule(c->session); 245 } 246 } 247 248 #define SET_NUM(p, x, v, min, max) \ 249 do { \ 250 if (!strcmp((p)->key, #v)) { \ 251 (x)->his.v = text_to_num((p)->value, (min), (max), &err); \ 252 if (err) { \ 253 log_warnx("bad param %s=%s: %s", \ 254 (p)->key, (p)->value, err); \ 255 errors++; \ 256 } \ 257 log_debug("SET_NUM: %s = %llu", #v, (u_int64_t)(x)->his.v); \ 258 } \ 259 } while (0) 260 261 #define SET_BOOL(p, x, v) \ 262 do { \ 263 if (!strcmp((p)->key, #v)) { \ 264 (x)->his.v = text_to_bool((p)->value, &err); \ 265 if (err) { \ 266 log_warnx("bad param %s=%s: %s", \ 267 (p)->key, (p)->value, err); \ 268 errors++; \ 269 } \ 270 log_debug("SET_BOOL: %s = %u", #v, (int)(x)->his.v); \ 271 } \ 272 } while (0) 273 274 int 275 conn_parse_kvp(struct connection *c, struct kvp *kvp) 276 { 277 struct kvp *k; 278 struct session *s = c->session; 279 const char *err; 280 int errors = 0; 281 282 283 for (k = kvp; k->key; k++) { 284 SET_NUM(k, s, MaxBurstLength, 512, 16777215); 285 SET_NUM(k, s, FirstBurstLength, 512, 16777215); 286 SET_NUM(k, s, DefaultTime2Wait, 0, 3600); 287 SET_NUM(k, s, DefaultTime2Retain, 0, 3600); 288 SET_NUM(k, s, MaxOutstandingR2T, 1, 65535); 289 SET_NUM(k, s, TargetPortalGroupTag, 1, 65535); 290 SET_NUM(k, s, MaxConnections, 1, 65535); 291 SET_BOOL(k, s, InitialR2T); 292 SET_BOOL(k, s, ImmediateData); 293 SET_BOOL(k, s, DataPDUInOrder); 294 SET_BOOL(k, s, DataSequenceInOrder); 295 SET_NUM(k, s, ErrorRecoveryLevel, 0, 2); 296 SET_NUM(k, c, MaxRecvDataSegmentLength, 512, 16777215); 297 } 298 299 if (errors) { 300 log_warnx("conn_parse_kvp: errors found"); 301 return -1; 302 } 303 return 0; 304 } 305 306 #undef SET_NUM 307 #undef SET_BOOL 308 309 int 310 conn_gen_kvp(struct connection *c, struct kvp *kvp, size_t *nkvp) 311 { 312 struct session *s = c->session; 313 size_t i = 0; 314 315 if (s->mine.MaxConnections != iscsi_sess_defaults.MaxConnections) { 316 i++; 317 if (kvp && i < *nkvp) { 318 kvp[i].key = strdup("MaxConnections"); 319 if (kvp[i].key == NULL) 320 return (-1); 321 if (asprintf(&kvp[i].value, "%u", 322 (unsigned int)s->mine.MaxConnections) == -1) { 323 kvp[i].value = NULL; 324 return (-1); 325 } 326 } 327 } 328 if (c->mine.MaxRecvDataSegmentLength != 329 iscsi_conn_defaults.MaxRecvDataSegmentLength) { 330 i++; 331 if (kvp && i < *nkvp) { 332 kvp[i].key = strdup("MaxRecvDataSegmentLength"); 333 if (kvp[i].key == NULL) 334 return (-1); 335 if (asprintf(&kvp[i].value, "%u", 336 (unsigned int)c->mine.MaxRecvDataSegmentLength) == -1) { 337 kvp[i].value = NULL; 338 return (-1); 339 } 340 } 341 } 342 343 *nkvp = i; 344 return (0); 345 } 346 347 void 348 conn_pdu_write(struct connection *c, struct pdu *p) 349 { 350 struct iscsi_pdu *ipdu; 351 352 /* XXX I GUESS THIS SHOULD BE MOVED TO PDU SOMEHOW... */ 353 ipdu = pdu_getbuf(p, NULL, PDU_HEADER); 354 switch (ISCSI_PDU_OPCODE(ipdu->opcode)) { 355 case ISCSI_OP_I_NOP: 356 case ISCSI_OP_SCSI_REQUEST: 357 case ISCSI_OP_TASK_REQUEST: 358 case ISCSI_OP_LOGIN_REQUEST: 359 case ISCSI_OP_TEXT_REQUEST: 360 case ISCSI_OP_DATA_OUT: 361 case ISCSI_OP_LOGOUT_REQUEST: 362 case ISCSI_OP_SNACK_REQUEST: 363 ipdu->expstatsn = ntohl(c->expstatsn); 364 break; 365 } 366 367 TAILQ_INSERT_TAIL(&c->pdu_w, p, entry); 368 event_add(&c->wev, NULL); 369 } 370 371 /* connection state machine more or less as specified in the RFC */ 372 struct { 373 int state; 374 enum c_event event; 375 int (*action)(struct connection *, enum c_event); 376 } fsm[] = { 377 { CONN_FREE, CONN_EV_CONNECT, c_do_connect }, /* T1 */ 378 { CONN_XPT_WAIT, CONN_EV_CONNECTED, c_do_login }, /* T4 */ 379 { CONN_IN_LOGIN, CONN_EV_LOGGED_IN, c_do_loggedin }, /* T5 */ 380 { CONN_LOGGED_IN, CONN_EV_LOGOUT, c_do_logout }, /* T9 */ 381 { CONN_LOGOUT_REQ, CONN_EV_LOGOUT, c_do_logout }, /* T10 */ 382 { CONN_IN_LOGOUT, CONN_EV_LOGGED_OUT, c_do_loggedout }, /* T13 */ 383 { CONN_ANYSTATE, CONN_EV_CLOSED, c_do_fail }, 384 { CONN_ANYSTATE, CONN_EV_FAIL, c_do_fail }, 385 { CONN_ANYSTATE, CONN_EV_FREE, c_do_fail }, 386 { 0, 0, NULL } 387 }; 388 389 void 390 conn_fsm(struct connection *c, enum c_event event) 391 { 392 int i, ns; 393 394 for (i = 0; fsm[i].action != NULL; i++) { 395 if (c->state & fsm[i].state && event == fsm[i].event) { 396 log_debug("conn_fsm[%s]: %s ev %s", 397 c->session->config.SessionName, 398 conn_state(c->state), conn_event(event)); 399 ns = fsm[i].action(c, event); 400 if (ns == -1) 401 /* XXX better please */ 402 fatalx("conn_fsm: action failed"); 403 log_debug("conn_fsm[%s]: new state %s", 404 c->session->config.SessionName, conn_state(ns)); 405 c->state = ns; 406 return; 407 } 408 } 409 log_warnx("conn_fsm[%s]: unhandled state transition [%s, %s]", 410 c->session->config.SessionName, conn_state(c->state), 411 conn_event(event)); 412 fatalx("bork bork bork"); 413 } 414 415 int 416 c_do_connect(struct connection *c, enum c_event ev) 417 { 418 if (c->fd == -1) { 419 log_warnx("connect(%s), lost socket", 420 log_sockaddr(&c->config.TargetAddr)); 421 session_fsm(c->session, SESS_EV_CONN_FAIL, c); 422 return (CONN_FREE); 423 } 424 425 if (connect(c->fd, (struct sockaddr *)&c->config.TargetAddr, 426 c->config.TargetAddr.ss_len) == -1) { 427 if (errno == EINPROGRESS) { 428 event_add(&c->wev, NULL); 429 return (CONN_XPT_WAIT); 430 } else { 431 log_warn("connect(%s)", 432 log_sockaddr(&c->config.TargetAddr)); 433 session_fsm(c->session, SESS_EV_CONN_FAIL, c); 434 return (CONN_FREE); 435 } 436 } 437 /* move forward */ 438 return (c_do_login(c, CONN_EV_CONNECTED)); 439 } 440 441 int 442 c_do_login(struct connection *c, enum c_event ev) 443 { 444 /* start a login session and hope for the best ... */ 445 initiator_login(c); 446 return (CONN_IN_LOGIN); 447 } 448 449 int 450 c_do_loggedin(struct connection *c, enum c_event ev) 451 { 452 session_fsm(c->session, SESS_EV_CONN_LOGGED_IN, c); 453 454 return (CONN_LOGGED_IN); 455 } 456 457 int 458 c_do_logout(struct connection *c, enum c_event ev) 459 { 460 /* logout is in progress ... */ 461 return (CONN_IN_LOGOUT); 462 } 463 464 int 465 c_do_loggedout(struct connection *c, enum c_event ev) 466 { 467 /* close TCP session and cleanup */ 468 event_del(&c->ev); 469 event_del(&c->wev); 470 close(c->fd); 471 472 /* session is informed by the logout handler */ 473 return (CONN_FREE); 474 } 475 476 int 477 c_do_fail(struct connection *c, enum c_event ev) 478 { 479 /* cleanup events so that the connection does not retrigger */ 480 event_del(&c->ev); 481 event_del(&c->wev); 482 close(c->fd); 483 484 session_fsm(c->session, SESS_EV_CONN_FAIL, c); 485 486 if (ev == CONN_EV_FREE || c->state & CONN_NEVER_LOGGED_IN) 487 return (CONN_FREE); 488 return (CONN_CLEANUP_WAIT); 489 } 490 491 const char * 492 conn_state(int s) 493 { 494 static char buf[15]; 495 496 switch (s) { 497 case CONN_FREE: 498 return "FREE"; 499 case CONN_XPT_WAIT: 500 return "XPT_WAIT"; 501 case CONN_XPT_UP: 502 return "XPT_UP"; 503 case CONN_IN_LOGIN: 504 return "IN_LOGIN"; 505 case CONN_LOGGED_IN: 506 return "LOGGED_IN"; 507 case CONN_IN_LOGOUT: 508 return "IN_LOGOUT"; 509 case CONN_LOGOUT_REQ: 510 return "LOGOUT_REQ"; 511 case CONN_CLEANUP_WAIT: 512 return "CLEANUP_WAIT"; 513 case CONN_IN_CLEANUP: 514 return "IN_CLEANUP"; 515 default: 516 snprintf(buf, sizeof(buf), "UKNWN %x", s); 517 return buf; 518 } 519 /* NOTREACHED */ 520 } 521 522 const char * 523 conn_event(enum c_event e) 524 { 525 static char buf[15]; 526 527 switch (e) { 528 case CONN_EV_FAIL: 529 return "fail"; 530 case CONN_EV_CONNECT: 531 return "connect"; 532 case CONN_EV_CONNECTED: 533 return "connected"; 534 case CONN_EV_LOGGED_IN: 535 return "logged in"; 536 case CONN_EV_LOGOUT: 537 return "logout"; 538 case CONN_EV_LOGGED_OUT: 539 return "logged out"; 540 case CONN_EV_CLOSED: 541 return "closed"; 542 case CONN_EV_FREE: 543 return "forced free"; 544 } 545 546 snprintf(buf, sizeof(buf), "UKNWN %d", e); 547 return buf; 548 } 549