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