| File: | src/usr.sbin/iscsid/connection.c |
| Warning: | line 237, column 3 Attempt to free released memory |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 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(*__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_FREE0x0001; | |||
| 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)do { (&c->pdu_w)->tqh_first = ((void *)0); (&c-> pdu_w)->tqh_last = &(&c->pdu_w)->tqh_first; } while (0); | |||
| 75 | TAILQ_INIT(&c->tasks)do { (&c->tasks)->tqh_first = ((void *)0); (&c-> tasks)->tqh_last = &(&c->tasks)->tqh_first; } while (0); | |||
| 76 | TAILQ_INSERT_TAIL(&s->connections, c, entry)do { (c)->entry.tqe_next = ((void *)0); (c)->entry.tqe_prev = (&s->connections)->tqh_last; *(&s->connections )->tqh_last = (c); (&s->connections)->tqh_last = &(c)->entry.tqe_next; } while (0); | |||
| 77 | ||||
| 78 | if (pdu_readbuf_set(&c->prbuf, PDU_READ_SIZE(256 * 1024))) { | |||
| 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_STREAM1, 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_TCP6, TCP_NODELAY0x01, &nodelay, | |||
| 99 | sizeof(nodelay)) == -1) | |||
| 100 | log_warn("conn_new: setting TCP_NODELAY"); | |||
| 101 | ||||
| 102 | event_set(&c->ev, c->fd, EV_READ0x02|EV_PERSIST0x10, conn_dispatch, c); | |||
| 103 | event_set(&c->wev, c->fd, EV_WRITE0x04, 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)do { if (((c)->entry.tqe_next) != ((void *)0)) (c)->entry .tqe_next->entry.tqe_prev = (c)->entry.tqe_prev; else ( &c->session->connections)->tqh_last = (c)->entry .tqe_prev; *(c)->entry.tqe_prev = (c)->entry.tqe_next; ; ; } while (0); | |||
| 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_READ0x02)) { | |||
| 134 | log_debug("spurious read call"); | |||
| 135 | return; | |||
| 136 | } | |||
| 137 | if ((n = pdu_read(c)) == -1) { | |||
| 138 | if (errno(*__errno()) == EAGAIN35 || errno(*__errno()) == ENOBUFS55 || | |||
| 139 | errno(*__errno()) == EINTR4) /* 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_WRITE0x04)) { | |||
| 162 | log_debug("spurious write call"); | |||
| 163 | return; | |||
| 164 | } | |||
| 165 | ||||
| 166 | switch (c->state) { | |||
| 167 | case CONN_XPT_WAIT0x0002: | |||
| 168 | len = sizeof(error); | |||
| 169 | if (getsockopt(c->fd, SOL_SOCKET0xffff, SO_ERROR0x1007, | |||
| 170 | &error, &len) == -1 || (errno(*__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((void *)0)); | |||
| 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(0x0010 | 0x0040)) && TAILQ_EMPTY(&c->tasks)(((&c->tasks)->tqh_first) == ((void *)0))) | |||
| 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)do { (t)->entry.tqe_next = ((void *)0); (t)->entry.tqe_prev = (&c->tasks)->tqh_last; *(&c->tasks)->tqh_last = (t); (&c->tasks)->tqh_last = &(t)->entry. tqe_next; } while (0); | |||
| ||||
| 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)((&c->tasks)->tqh_first); | |||
| 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)((&t->sendq)->tqh_first); p != NULL((void *)0); p = np) { | |||
| 230 | np = TAILQ_NEXT(p, entry)((p)->entry.tqe_next); | |||
| 231 | TAILQ_REMOVE(&t->sendq, p, entry)do { if (((p)->entry.tqe_next) != ((void *)0)) (p)->entry .tqe_next->entry.tqe_prev = (p)->entry.tqe_prev; else ( &t->sendq)->tqh_last = (p)->entry.tqe_prev; *(p) ->entry.tqe_prev = (p)->entry.tqe_next; ; ; } while (0); | |||
| 232 | conn_pdu_write(c, p); | |||
| 233 | } | |||
| 234 | if (t->callback == NULL((void *)0)) { | |||
| 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)do { if (((t)->entry.tqe_next) != ((void *)0)) (t)->entry .tqe_next->entry.tqe_prev = (t)->entry.tqe_prev; else ( &c->tasks)->tqh_last = (t)->entry.tqe_prev; *(t) ->entry.tqe_prev = (t)->entry.tqe_next; ; ; } while (0); | |||
| 249 | if (!TAILQ_EMPTY(&c->tasks)(((&c->tasks)->tqh_first) == ((void *)0))) | |||
| 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((void *)0)) | |||
| 329 | return -1; | |||
| 330 | if (asprintf(&kvp[i].value, "%hu", | |||
| 331 | s->mine.MaxConnections) == -1) { | |||
| 332 | kvp[i].value = NULL((void *)0); | |||
| 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((void *)0)) | |||
| 343 | return -1; | |||
| 344 | if (asprintf(&kvp[i].value, "%u", | |||
| 345 | c->mine.MaxRecvDataSegmentLength) == -1) { | |||
| 346 | kvp[i].value = NULL((void *)0); | |||
| 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((void *)0), PDU_HEADER0); | |||
| 364 | switch (ISCSI_PDU_OPCODE(ipdu->opcode)((ipdu->opcode) & 0x3f)) { | |||
| 365 | case ISCSI_OP_I_NOP0x00: | |||
| 366 | case ISCSI_OP_SCSI_REQUEST0x01: | |||
| 367 | case ISCSI_OP_TASK_REQUEST0x02: | |||
| 368 | case ISCSI_OP_LOGIN_REQUEST0x03: | |||
| 369 | case ISCSI_OP_TEXT_REQUEST0x04: | |||
| 370 | case ISCSI_OP_DATA_OUT0x05: | |||
| 371 | case ISCSI_OP_LOGOUT_REQUEST0x06: | |||
| 372 | case ISCSI_OP_SNACK_REQUEST0x10: | |||
| 373 | ipdu->expstatsn = ntohl(c->expstatsn)(__uint32_t)(__builtin_constant_p(c->expstatsn) ? (__uint32_t )(((__uint32_t)(c->expstatsn) & 0xff) << 24 | (( __uint32_t)(c->expstatsn) & 0xff00) << 8 | ((__uint32_t )(c->expstatsn) & 0xff0000) >> 8 | ((__uint32_t) (c->expstatsn) & 0xff000000) >> 24) : __swap32md (c->expstatsn)); | |||
| 374 | break; | |||
| 375 | } | |||
| 376 | ||||
| 377 | TAILQ_INSERT_TAIL(&c->pdu_w, p, entry)do { (p)->entry.tqe_next = ((void *)0); (p)->entry.tqe_prev = (&c->pdu_w)->tqh_last; *(&c->pdu_w)->tqh_last = (p); (&c->pdu_w)->tqh_last = &(p)->entry. tqe_next; } while (0); | |||
| 378 | event_add(&c->wev, NULL((void *)0)); | |||
| 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_FREE0x0001, CONN_EV_CONNECT, c_do_connect }, /* T1 */ | |||
| 388 | { CONN_XPT_WAIT0x0002, CONN_EV_CONNECTED, c_do_login }, /* T4 */ | |||
| 389 | { CONN_IN_LOGIN0x0008, CONN_EV_LOGGED_IN, c_do_loggedin }, /* T5 */ | |||
| 390 | { CONN_LOGGED_IN0x0010, CONN_EV_LOGOUT, c_do_logout }, /* T9 */ | |||
| 391 | { CONN_LOGGED_IN0x0010, CONN_EV_REQ_LOGOUT, c_do_req_logout },/* T11 */ | |||
| 392 | { CONN_LOGOUT_REQ0x0040, CONN_EV_LOGOUT, c_do_logout }, /* T10 */ | |||
| 393 | { CONN_LOGOUT_REQ0x0040, CONN_EV_REQ_LOGOUT, c_do_req_logout},/* T12 */ | |||
| 394 | { CONN_LOGOUT_REQ0x0040, CONN_EV_LOGGED_OUT, c_do_loggedout },/* T18 */ | |||
| 395 | { CONN_IN_LOGOUT0x0020, CONN_EV_LOGGED_OUT, c_do_loggedout }, /* T13 */ | |||
| 396 | { CONN_IN_LOGOUT0x0020, CONN_EV_REQ_LOGOUT, c_do_req_logout },/* T14 */ | |||
| 397 | { CONN_CLEANUP_WAIT0x0080, CONN_EV_CLEANING_UP, c_do_cleanup},/* M2 */ | |||
| 398 | { CONN_CLEANUP_WAIT0x0080, CONN_EV_FREE, c_do_loggedout }, /* M1 */ | |||
| 399 | { CONN_IN_CLEANUP0x0100, CONN_EV_FREE, c_do_loggedout }, /* M4 */ | |||
| 400 | { CONN_IN_CLEANUP0x0100, CONN_EV_CLEANING_UP, c_do_cleanup}, | |||
| 401 | /* either one of T2, T7, T15, T16, T17, M3 */ | |||
| 402 | { CONN_ANYSTATE0xffff, CONN_EV_CLOSED, c_do_fail }, | |||
| 403 | { CONN_ANYSTATE0xffff, CONN_EV_FAIL, c_do_fail }, | |||
| 404 | { CONN_ANYSTATE0xffff, CONN_EV_FREE, c_do_fail }, | |||
| 405 | { 0, 0, NULL((void *)0) } | |||
| 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((void *)0); 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_FREE0x0001; | |||
| 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_FREE0x0001; | |||
| 450 | } | |||
| 451 | } | |||
| 452 | if (connect(c->fd, (struct sockaddr *)&c->config.TargetAddr, | |||
| 453 | c->config.TargetAddr.ss_len) == -1) { | |||
| 454 | if (errno(*__errno()) == EINPROGRESS36) { | |||
| 455 | event_add(&c->wev, NULL((void *)0)); | |||
| 456 | event_add(&c->ev, NULL((void *)0)); | |||
| 457 | return CONN_XPT_WAIT0x0002; | |||
| 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_FREE0x0001; | |||
| 463 | } | |||
| 464 | } | |||
| 465 | event_add(&c->ev, NULL((void *)0)); | |||
| 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_LOGIN0x0008; | |||
| 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_IN0x0010; | |||
| 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_LOGOUT0x0020) | |||
| 493 | return CONN_IN_LOGOUT0x0020; | |||
| 494 | else | |||
| 495 | return CONN_LOGOUT_REQ0x0040; | |||
| 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_LOGOUT0x0020; | |||
| 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_FREE0x0001; | |||
| 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(0x0001 | 0x0002 | 0x0004 | 0x0008)) | |||
| 533 | return CONN_FREE0x0001; | |||
| 534 | return CONN_CLEANUP_WAIT0x0080; | |||
| 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_CLEANUP0x0100; | |||
| 542 | } | |||
| 543 | ||||
| 544 | const char * | |||
| 545 | conn_state(int s) | |||
| 546 | { | |||
| 547 | static char buf[15]; | |||
| 548 | ||||
| 549 | switch (s) { | |||
| 550 | case CONN_FREE0x0001: | |||
| 551 | return "FREE"; | |||
| 552 | case CONN_XPT_WAIT0x0002: | |||
| 553 | return "XPT_WAIT"; | |||
| 554 | case CONN_XPT_UP0x0004: | |||
| 555 | return "XPT_UP"; | |||
| 556 | case CONN_IN_LOGIN0x0008: | |||
| 557 | return "IN_LOGIN"; | |||
| 558 | case CONN_LOGGED_IN0x0010: | |||
| 559 | return "LOGGED_IN"; | |||
| 560 | case CONN_IN_LOGOUT0x0020: | |||
| 561 | return "IN_LOGOUT"; | |||
| 562 | case CONN_LOGOUT_REQ0x0040: | |||
| 563 | return "LOGOUT_REQ"; | |||
| 564 | case CONN_CLEANUP_WAIT0x0080: | |||
| 565 | return "CLEANUP_WAIT"; | |||
| 566 | case CONN_IN_CLEANUP0x0100: | |||
| 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 | } |