|
@@ -18,6 +18,7 @@
|
18
|
18
|
|
19
|
19
|
#include <stddef.h>
|
20
|
20
|
#include <string.h>
|
|
21
|
+#include <stdlib.h>
|
21
|
22
|
#include <vsprintf.h>
|
22
|
23
|
#include <errno.h>
|
23
|
24
|
#include <assert.h>
|
|
@@ -37,6 +38,46 @@ static void iscsi_start_tx ( struct iscsi_session *iscsi );
|
37
|
38
|
static void iscsi_start_data_out ( struct iscsi_session *iscsi,
|
38
|
39
|
unsigned int datasn );
|
39
|
40
|
|
|
41
|
+/**
|
|
42
|
+ * Mark iSCSI operation as complete
|
|
43
|
+ *
|
|
44
|
+ * @v iscsi iSCSI session
|
|
45
|
+ * @v rc Return status code
|
|
46
|
+ *
|
|
47
|
+ * Note that iscsi_done() will not close the connection, and must
|
|
48
|
+ * therefore be called only when the internal state machines are in an
|
|
49
|
+ * appropriate state, otherwise bad things may happen on the next call
|
|
50
|
+ * to iscsi_issue(). The general rule is to call iscsi_done() only at
|
|
51
|
+ * the end of receiving a PDU; at this point the TX and RX engines
|
|
52
|
+ * should both be idle.
|
|
53
|
+ */
|
|
54
|
+static void iscsi_done ( struct iscsi_session *iscsi, int rc ) {
|
|
55
|
+ /* Clear current SCSI command */
|
|
56
|
+ iscsi->command = NULL;
|
|
57
|
+ /* Free any memory that may have been used for CHAP */
|
|
58
|
+ chap_finish ( &iscsi->chap );
|
|
59
|
+ /* Mark asynchronous operation as complete */
|
|
60
|
+ async_done ( &iscsi->aop, rc );
|
|
61
|
+}
|
|
62
|
+
|
|
63
|
+/**
|
|
64
|
+ * Mark iSCSI operation as complete, and close TCP connection
|
|
65
|
+ *
|
|
66
|
+ * @v iscsi iSCSI session
|
|
67
|
+ * @v rc Return status code
|
|
68
|
+ */
|
|
69
|
+static void iscsi_close ( struct iscsi_session *iscsi, int rc ) {
|
|
70
|
+
|
|
71
|
+ /* Clear session status */
|
|
72
|
+ iscsi->status = 0;
|
|
73
|
+
|
|
74
|
+ /* Close TCP connection */
|
|
75
|
+ tcp_close ( &iscsi->tcp );
|
|
76
|
+
|
|
77
|
+ /* Mark iSCSI operation as complete */
|
|
78
|
+ iscsi_done ( iscsi, rc );
|
|
79
|
+}
|
|
80
|
+
|
40
|
81
|
/****************************************************************************
|
41
|
82
|
*
|
42
|
83
|
* iSCSI SCSI command issuing
|
|
@@ -112,9 +153,11 @@ static void iscsi_rx_scsi_response ( struct iscsi_session *iscsi, void *data,
|
112
|
153
|
iscsi->command->status = response->status;
|
113
|
154
|
|
114
|
155
|
/* Mark as completed, with error if applicable */
|
115
|
|
- iscsi->status |= ISCSI_STATUS_DONE;
|
116
|
|
- if ( response->response != ISCSI_RESPONSE_COMMAND_COMPLETE )
|
117
|
|
- iscsi->status |= ISCSI_STATUS_ERR;
|
|
156
|
+ if ( response->response == ISCSI_RESPONSE_COMMAND_COMPLETE ) {
|
|
157
|
+ iscsi_done ( iscsi, 0 );
|
|
158
|
+ } else {
|
|
159
|
+ iscsi_done ( iscsi, -EIO );
|
|
160
|
+ }
|
118
|
161
|
}
|
119
|
162
|
|
120
|
163
|
/**
|
|
@@ -146,7 +189,7 @@ static void iscsi_rx_data_in ( struct iscsi_session *iscsi, void *data,
|
146
|
189
|
if ( ( offset + len ) == iscsi->command->data_in_len ) {
|
147
|
190
|
assert ( data_in->flags & ISCSI_FLAG_FINAL );
|
148
|
191
|
assert ( remaining == 0 );
|
149
|
|
- iscsi->status |= ISCSI_STATUS_DONE;
|
|
192
|
+ iscsi_done ( iscsi, 0 );
|
150
|
193
|
}
|
151
|
194
|
}
|
152
|
195
|
|
|
@@ -258,6 +301,32 @@ static void iscsi_tx_data_out ( struct iscsi_session *iscsi,
|
258
|
301
|
*
|
259
|
302
|
*/
|
260
|
303
|
|
|
304
|
+/**
|
|
305
|
+ * Version of snprintf() that accepts a signed buffer size
|
|
306
|
+ *
|
|
307
|
+ * @v buf Buffer into which to write the string
|
|
308
|
+ * @v size Size of buffer
|
|
309
|
+ * @v fmt Format string
|
|
310
|
+ * @v args Arguments corresponding to the format string
|
|
311
|
+ * @ret len Length of formatted string
|
|
312
|
+ *
|
|
313
|
+ * This is a utility function for iscsi_build_login_request_strings().
|
|
314
|
+ */
|
|
315
|
+static int ssnprintf ( char *buf, ssize_t ssize, const char *fmt, ... ) {
|
|
316
|
+ va_list args;
|
|
317
|
+ int len;
|
|
318
|
+
|
|
319
|
+ /* Treat negative buffer size as zero buffer size */
|
|
320
|
+ if ( ssize < 0 )
|
|
321
|
+ ssize = 0;
|
|
322
|
+
|
|
323
|
+ /* Hand off to vsnprintf */
|
|
324
|
+ va_start ( args, fmt );
|
|
325
|
+ len = vsnprintf ( buf, ssize, fmt, args );
|
|
326
|
+ va_end ( args );
|
|
327
|
+ return len;
|
|
328
|
+}
|
|
329
|
+
|
261
|
330
|
/**
|
262
|
331
|
* Build iSCSI login request strings
|
263
|
332
|
*
|
|
@@ -291,45 +360,57 @@ static void iscsi_tx_data_out ( struct iscsi_session *iscsi,
|
291
|
360
|
*/
|
292
|
361
|
static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi,
|
293
|
362
|
void *data, size_t len ) {
|
294
|
|
- struct iscsi_bhs_login_request *request = &iscsi->tx_bhs.login_request;
|
|
363
|
+ unsigned int used = 0;
|
|
364
|
+ unsigned int i;
|
|
365
|
+
|
|
366
|
+ if ( iscsi->status & ISCSI_STATUS_STRINGS_SECURITY ) {
|
|
367
|
+ used += ssnprintf ( data + used, len - used,
|
|
368
|
+ "InitiatorName=%s%c"
|
|
369
|
+ "TargetName=%s%c"
|
|
370
|
+ "SessionType=Normal%c"
|
|
371
|
+ "AuthMethod=CHAP,None%c",
|
|
372
|
+ iscsi->initiator, 0, iscsi->target, 0,
|
|
373
|
+ 0, 0 );
|
|
374
|
+ }
|
295
|
375
|
|
296
|
|
- switch ( request->flags & ISCSI_LOGIN_CSG_MASK ) {
|
297
|
|
- case ISCSI_LOGIN_CSG_SECURITY_NEGOTIATION:
|
298
|
|
- return snprintf ( data, len,
|
299
|
|
- "InitiatorName=%s%c"
|
300
|
|
- "TargetName=%s%c"
|
301
|
|
- "SessionType=Normal%c"
|
302
|
|
- "AuthMethod=CHAP,None%c"
|
303
|
|
- "CHAP_A=5%c",
|
304
|
|
- iscsi->initiator, 0, iscsi->target, 0,
|
305
|
|
- 0, 0, 0 );
|
306
|
|
- case ISCSI_LOGIN_CSG_OPERATIONAL_NEGOTIATION:
|
307
|
|
- return snprintf ( data, len,
|
308
|
|
- "HeaderDigest=None%c"
|
309
|
|
- "DataDigest=None%c"
|
310
|
|
- "InitialR2T=Yes%c"
|
311
|
|
- "DefaultTime2Wait=0%c"
|
312
|
|
- "DefaultTime2Retain=0%c"
|
313
|
|
- "MaxOutstandingR2T=1%c"
|
314
|
|
- "DataPDUInOrder=Yes%c"
|
315
|
|
- "DataSequenceInOrder=Yes%c"
|
316
|
|
- "ErrorRecoveryLevel=0%c",
|
317
|
|
- 0, 0, 0, 0, 0, 0, 0, 0, 0 );
|
318
|
|
- default:
|
319
|
|
- assert ( 0 );
|
320
|
|
- return 0;
|
|
376
|
+ if ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_ALGORITHM ) {
|
|
377
|
+ used += ssnprintf ( data + used, len - used, "CHAP_A=5%c", 0 );
|
|
378
|
+ }
|
|
379
|
+
|
|
380
|
+ if ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_RESPONSE ) {
|
|
381
|
+ used += ssnprintf ( data + used, len - used,
|
|
382
|
+ "CHAP_N=%s%cCHAP_R=0x",
|
|
383
|
+ iscsi->username, 0 );
|
|
384
|
+ for ( i = 0 ; i < iscsi->chap.response_len ; i++ ) {
|
|
385
|
+ used += ssnprintf ( data + used, len - used, "%02x",
|
|
386
|
+ iscsi->chap.response[i] );
|
|
387
|
+ }
|
|
388
|
+ used += ssnprintf ( data + used, len - used, "%c", 0 );
|
|
389
|
+ }
|
|
390
|
+
|
|
391
|
+ if ( iscsi->status & ISCSI_STATUS_STRINGS_OPERATIONAL ) {
|
|
392
|
+ used += ssnprintf ( data + used, len - used,
|
|
393
|
+ "HeaderDigest=None%c"
|
|
394
|
+ "DataDigest=None%c"
|
|
395
|
+ "InitialR2T=Yes%c"
|
|
396
|
+ "DefaultTime2Wait=0%c"
|
|
397
|
+ "DefaultTime2Retain=0%c"
|
|
398
|
+ "MaxOutstandingR2T=1%c"
|
|
399
|
+ "DataPDUInOrder=Yes%c"
|
|
400
|
+ "DataSequenceInOrder=Yes%c"
|
|
401
|
+ "ErrorRecoveryLevel=0%c",
|
|
402
|
+ 0, 0, 0, 0, 0, 0, 0, 0, 0 );
|
321
|
403
|
}
|
|
404
|
+
|
|
405
|
+ return used;
|
322
|
406
|
}
|
323
|
407
|
|
324
|
408
|
/**
|
325
|
409
|
* Build iSCSI login request BHS
|
326
|
410
|
*
|
327
|
411
|
* @v iscsi iSCSI session
|
328
|
|
- * @v stage Current stage of iSCSI login
|
329
|
|
- * @v send_strings Send login strings with this login request
|
330
|
412
|
*/
|
331
|
|
-static void iscsi_start_login ( struct iscsi_session *iscsi,
|
332
|
|
- int stage, int send_strings ) {
|
|
413
|
+static void iscsi_start_login ( struct iscsi_session *iscsi ) {
|
333
|
414
|
struct iscsi_bhs_login_request *request = &iscsi->tx_bhs.login_request;
|
334
|
415
|
int len;
|
335
|
416
|
|
|
@@ -337,13 +418,11 @@ static void iscsi_start_login ( struct iscsi_session *iscsi,
|
337
|
418
|
iscsi_start_tx ( iscsi );
|
338
|
419
|
request->opcode = ( ISCSI_OPCODE_LOGIN_REQUEST |
|
339
|
420
|
ISCSI_FLAG_IMMEDIATE );
|
340
|
|
- request->flags = ( ISCSI_LOGIN_FLAG_TRANSITION | stage );
|
341
|
|
-
|
|
421
|
+ request->flags = ( ( iscsi->status & ISCSI_STATUS_PHASE_MASK ) |
|
|
422
|
+ ISCSI_LOGIN_FLAG_TRANSITION );
|
342
|
423
|
/* version_max and version_min left as zero */
|
343
|
|
- if ( send_strings ) {
|
344
|
|
- len = iscsi_build_login_request_strings ( iscsi, NULL, 0 );
|
345
|
|
- ISCSI_SET_LENGTHS ( request->lengths, 0, len );
|
346
|
|
- }
|
|
424
|
+ len = iscsi_build_login_request_strings ( iscsi, NULL, 0 );
|
|
425
|
+ ISCSI_SET_LENGTHS ( request->lengths, 0, len );
|
347
|
426
|
request->isid_iana_en = htonl ( ISCSI_ISID_IANA |
|
348
|
427
|
IANA_EN_FEN_SYSTEMS );
|
349
|
428
|
/* isid_iana_qual left as zero */
|
|
@@ -354,6 +433,18 @@ static void iscsi_start_login ( struct iscsi_session *iscsi,
|
354
|
433
|
request->expstatsn = htonl ( iscsi->statsn + 1 );
|
355
|
434
|
}
|
356
|
435
|
|
|
436
|
+/**
|
|
437
|
+ * Complete iSCSI login request PDU transmission
|
|
438
|
+ *
|
|
439
|
+ * @v iscsi iSCSI session
|
|
440
|
+ *
|
|
441
|
+ */
|
|
442
|
+static void iscsi_login_request_done ( struct iscsi_session *iscsi ) {
|
|
443
|
+
|
|
444
|
+ /* Clear any "strings to send" flags */
|
|
445
|
+ iscsi->status &= ~ISCSI_STATUS_STRINGS_MASK;
|
|
446
|
+}
|
|
447
|
+
|
357
|
448
|
/**
|
358
|
449
|
* Transmit data segment of an iSCSI login request PDU
|
359
|
450
|
*
|
|
@@ -370,6 +461,238 @@ static void iscsi_tx_login_request ( struct iscsi_session *iscsi,
|
370
|
461
|
len - iscsi->tx_offset );
|
371
|
462
|
}
|
372
|
463
|
|
|
464
|
+/**
|
|
465
|
+ * Handle iSCSI AuthMethod text value
|
|
466
|
+ *
|
|
467
|
+ * @v iscsi iSCSI session
|
|
468
|
+ * @v finished Value is complete
|
|
469
|
+ */
|
|
470
|
+static void iscsi_handle_authmethod_value ( struct iscsi_session *iscsi,
|
|
471
|
+ int finished ) {
|
|
472
|
+ struct iscsi_string_state *string = &iscsi->string;
|
|
473
|
+
|
|
474
|
+ if ( ! finished )
|
|
475
|
+ return;
|
|
476
|
+
|
|
477
|
+ /* If server requests CHAP, send the CHAP_A string */
|
|
478
|
+ if ( strcmp ( string->value, "CHAP" ) == 0 ) {
|
|
479
|
+ DBG ( "iSCSI %p initiating CHAP authentication\n", iscsi );
|
|
480
|
+ iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_ALGORITHM;
|
|
481
|
+ }
|
|
482
|
+}
|
|
483
|
+
|
|
484
|
+/**
|
|
485
|
+ * Handle iSCSI CHAP_A text value
|
|
486
|
+ *
|
|
487
|
+ * @v iscsi iSCSI session
|
|
488
|
+ * @v finished Value is complete
|
|
489
|
+ */
|
|
490
|
+static void iscsi_handle_chap_a_value ( struct iscsi_session *iscsi,
|
|
491
|
+ int finished ) {
|
|
492
|
+ struct iscsi_string_state *string = &iscsi->string;
|
|
493
|
+ int rc;
|
|
494
|
+
|
|
495
|
+ if ( ! finished )
|
|
496
|
+ return;
|
|
497
|
+
|
|
498
|
+ /* We only ever offer "5" (i.e. MD5) as an algorithm, so if
|
|
499
|
+ * the server responds with anything else it is a protocol
|
|
500
|
+ * violation.
|
|
501
|
+ */
|
|
502
|
+ if ( strcmp ( string->value, "5" ) != 0 ) {
|
|
503
|
+ DBG ( "iSCSI %p got invalid CHAP algorithm \"%s\"\n",
|
|
504
|
+ iscsi, string->value );
|
|
505
|
+ }
|
|
506
|
+
|
|
507
|
+ /* Prepare for CHAP with MD5 */
|
|
508
|
+ if ( ( rc = chap_init ( &iscsi->chap, &md5_algorithm ) ) != 0 ) {
|
|
509
|
+ DBG ( "iSCSI %p could not initialise CHAP\n", iscsi );
|
|
510
|
+ iscsi_close ( iscsi, rc );
|
|
511
|
+ }
|
|
512
|
+}
|
|
513
|
+
|
|
514
|
+/**
|
|
515
|
+ * Handle iSCSI CHAP_I text value
|
|
516
|
+ *
|
|
517
|
+ * @v iscsi iSCSI session
|
|
518
|
+ * @v finished Value is complete
|
|
519
|
+ */
|
|
520
|
+static void iscsi_handle_chap_i_value ( struct iscsi_session *iscsi,
|
|
521
|
+ int finished ) {
|
|
522
|
+ struct iscsi_string_state *string = &iscsi->string;
|
|
523
|
+ unsigned int identifier;
|
|
524
|
+ char *endp;
|
|
525
|
+
|
|
526
|
+ if ( ! finished )
|
|
527
|
+ return;
|
|
528
|
+
|
|
529
|
+ /* The CHAP identifier is an integer value */
|
|
530
|
+ identifier = strtoul ( string->value, &endp, 0 );
|
|
531
|
+ if ( *endp != '\0' ) {
|
|
532
|
+ DBG ( "iSCSI %p saw invalid CHAP identifier \"%s\"\n",
|
|
533
|
+ iscsi, string->value );
|
|
534
|
+ }
|
|
535
|
+
|
|
536
|
+ /* Identifier and secret are the first two components of the
|
|
537
|
+ * challenge.
|
|
538
|
+ */
|
|
539
|
+ chap_set_identifier ( &iscsi->chap, identifier );
|
|
540
|
+ chap_update ( &iscsi->chap, iscsi->password,
|
|
541
|
+ strlen ( iscsi->password ) );
|
|
542
|
+}
|
|
543
|
+
|
|
544
|
+/**
|
|
545
|
+ * Handle iSCSI CHAP_C text value
|
|
546
|
+ *
|
|
547
|
+ * @v iscsi iSCSI session
|
|
548
|
+ * @v finished Value is complete
|
|
549
|
+ */
|
|
550
|
+static void iscsi_handle_chap_c_value ( struct iscsi_session *iscsi,
|
|
551
|
+ int finished ) {
|
|
552
|
+ struct iscsi_string_state *string = &iscsi->string;
|
|
553
|
+ uint8_t byte;
|
|
554
|
+ char *endp;
|
|
555
|
+
|
|
556
|
+ /* Once the whole challenge is received, calculate the response */
|
|
557
|
+ if ( finished ) {
|
|
558
|
+ DBG ( "iSCSI %p sending CHAP response\n", iscsi );
|
|
559
|
+ chap_respond ( &iscsi->chap );
|
|
560
|
+ iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_RESPONSE;
|
|
561
|
+ return;
|
|
562
|
+ }
|
|
563
|
+
|
|
564
|
+ /* Wait until a complete octet ("0x??") is received */
|
|
565
|
+ if ( string->index != 4 )
|
|
566
|
+ return;
|
|
567
|
+
|
|
568
|
+ /* Add octet to challenge */
|
|
569
|
+ byte = strtoul ( string->value, &endp, 0 );
|
|
570
|
+ if ( *endp != '\0' ) {
|
|
571
|
+ DBG ( "iSCSI %p saw invalid CHAP challenge portion \"%s\"\n",
|
|
572
|
+ iscsi, string->value );
|
|
573
|
+ }
|
|
574
|
+ chap_update ( &iscsi->chap, &byte, sizeof ( byte ) );
|
|
575
|
+
|
|
576
|
+ /* Reset value back to "0x" */
|
|
577
|
+ string->index = 2;
|
|
578
|
+}
|
|
579
|
+
|
|
580
|
+/** An iSCSI text string that we want to handle */
|
|
581
|
+struct iscsi_string_type {
|
|
582
|
+ /** String name
|
|
583
|
+ *
|
|
584
|
+ * This is the portion before the "=" sign,
|
|
585
|
+ * e.g. InitiatorName, CHAP_A, etc.
|
|
586
|
+ */
|
|
587
|
+ const char *name;
|
|
588
|
+ /** Handle iSCSI string value
|
|
589
|
+ *
|
|
590
|
+ * @v iscsi iSCSI session
|
|
591
|
+ * @v finished Value is complete
|
|
592
|
+ *
|
|
593
|
+ * Process the string in @c iscsi->string. This method will
|
|
594
|
+ * be called once for each character in the string, and once
|
|
595
|
+ * again at the end of the string.
|
|
596
|
+ */
|
|
597
|
+ void ( * handle_value ) ( struct iscsi_session *iscsi, int finished );
|
|
598
|
+};
|
|
599
|
+
|
|
600
|
+/** iSCSI text strings that we want to handle */
|
|
601
|
+struct iscsi_string_type iscsi_string_types[] = {
|
|
602
|
+ { "AuthMethod", iscsi_handle_authmethod_value },
|
|
603
|
+ { "CHAP_A", iscsi_handle_chap_a_value },
|
|
604
|
+ { "CHAP_I", iscsi_handle_chap_i_value },
|
|
605
|
+ { "CHAP_C", iscsi_handle_chap_c_value },
|
|
606
|
+ { NULL, NULL }
|
|
607
|
+};
|
|
608
|
+
|
|
609
|
+/**
|
|
610
|
+ * Handle iSCSI string value
|
|
611
|
+ *
|
|
612
|
+ * @v iscsi iSCSI session
|
|
613
|
+ * @v finished Value is complete
|
|
614
|
+ *
|
|
615
|
+ * Process the string in @c iscsi->string. This function will be
|
|
616
|
+ * called once for each character in the string, and once again at the
|
|
617
|
+ * end of the string.
|
|
618
|
+ */
|
|
619
|
+static void iscsi_handle_string_value ( struct iscsi_session *iscsi,
|
|
620
|
+ int finished ) {
|
|
621
|
+ struct iscsi_string_state *string = &iscsi->string;
|
|
622
|
+ struct iscsi_string_type *type = iscsi_string_types;
|
|
623
|
+
|
|
624
|
+ assert ( string->key_value == STRING_VALUE );
|
|
625
|
+
|
|
626
|
+ for ( type = iscsi_string_types ; type->name ; type++ ) {
|
|
627
|
+ if ( strcmp ( type->name, string->key ) == 0 ) {
|
|
628
|
+ if ( string->index <= 1 ) {
|
|
629
|
+ DBG ( "iSCSI %p handling key \"%s\"\n",
|
|
630
|
+ iscsi, string->key );
|
|
631
|
+ }
|
|
632
|
+ type->handle_value ( iscsi, finished );
|
|
633
|
+ return;
|
|
634
|
+ }
|
|
635
|
+ }
|
|
636
|
+ if ( string->index <= 1 )
|
|
637
|
+ DBG ( "iSCSI %p ignoring key \"%s\"\n", iscsi, string->key );
|
|
638
|
+}
|
|
639
|
+
|
|
640
|
+/**
|
|
641
|
+ * Handle byte of an iSCSI string
|
|
642
|
+ *
|
|
643
|
+ * @v iscsi iSCSI session
|
|
644
|
+ * @v byte Byte of string
|
|
645
|
+ *
|
|
646
|
+ * Strings are handled a byte at a time in order to simplify the
|
|
647
|
+ * logic, and to ensure that we can provably cope with the TCP packet
|
|
648
|
+ * boundaries coming at inconvenient points, such as halfway through a
|
|
649
|
+ * string.
|
|
650
|
+ */
|
|
651
|
+static void iscsi_handle_string_byte ( struct iscsi_session *iscsi,
|
|
652
|
+ uint8_t byte ) {
|
|
653
|
+ struct iscsi_string_state *string = &iscsi->string;
|
|
654
|
+
|
|
655
|
+ if ( string->key_value == STRING_KEY ) {
|
|
656
|
+ switch ( byte ) {
|
|
657
|
+ case '\0':
|
|
658
|
+ /* Premature termination */
|
|
659
|
+ DBG ( "iSCSI %p premature key termination on \"%s\"\n",
|
|
660
|
+ iscsi, string->key );
|
|
661
|
+ string->index = 0;
|
|
662
|
+ break;
|
|
663
|
+ case '=':
|
|
664
|
+ /* End of key */
|
|
665
|
+ string->key_value = STRING_VALUE;
|
|
666
|
+ string->index = 0;
|
|
667
|
+ break;
|
|
668
|
+ default:
|
|
669
|
+ /* Part of key */
|
|
670
|
+ if ( string->index < ( sizeof ( string->key ) - 1 ) ) {
|
|
671
|
+ string->key[string->index++] = byte;
|
|
672
|
+ string->key[string->index] = '\0';
|
|
673
|
+ }
|
|
674
|
+ break;
|
|
675
|
+ }
|
|
676
|
+ } else {
|
|
677
|
+ switch ( byte ) {
|
|
678
|
+ case '\0':
|
|
679
|
+ /* End of string */
|
|
680
|
+ iscsi_handle_string_value ( iscsi, 1 );
|
|
681
|
+ string->key_value = STRING_KEY;
|
|
682
|
+ string->index = 0;
|
|
683
|
+ break;
|
|
684
|
+ default:
|
|
685
|
+ /* Part of value */
|
|
686
|
+ if ( string->index < ( sizeof ( string->value ) - 1 )){
|
|
687
|
+ string->value[string->index++] = byte;
|
|
688
|
+ string->value[string->index] = '\0';
|
|
689
|
+ iscsi_handle_string_value ( iscsi, 0 );
|
|
690
|
+ }
|
|
691
|
+ break;
|
|
692
|
+ }
|
|
693
|
+ }
|
|
694
|
+}
|
|
695
|
+
|
373
|
696
|
/**
|
374
|
697
|
* Receive data segment of an iSCSI login response PDU
|
375
|
698
|
*
|
|
@@ -379,10 +702,8 @@ static void iscsi_tx_login_request ( struct iscsi_session *iscsi,
|
379
|
702
|
* @v remaining Data remaining after this data
|
380
|
703
|
*
|
381
|
704
|
*/
|
382
|
|
-static void iscsi_rx_login_response ( struct iscsi_session *iscsi,
|
383
|
|
- void *data __unused,
|
384
|
|
- size_t len __unused,
|
385
|
|
- size_t remaining __unused ) {
|
|
705
|
+static void iscsi_rx_login_response ( struct iscsi_session *iscsi, void *data,
|
|
706
|
+ size_t len, size_t remaining ) {
|
386
|
707
|
struct iscsi_bhs_login_response *response
|
387
|
708
|
= &iscsi->rx_bhs.login_response;
|
388
|
709
|
|
|
@@ -390,33 +711,49 @@ static void iscsi_rx_login_response ( struct iscsi_session *iscsi,
|
390
|
711
|
if ( response->status_class != 0 ) {
|
391
|
712
|
printf ( "iSCSI login failure: class %02x detail %02x\n",
|
392
|
713
|
response->status_class, response->status_detail );
|
393
|
|
- iscsi->status |= ( ISCSI_STATUS_DONE | ISCSI_STATUS_ERR );
|
394
|
|
- tcp_close ( &iscsi->tcp );
|
|
714
|
+ iscsi_close ( iscsi, -EPERM );
|
395
|
715
|
return;
|
396
|
716
|
}
|
397
|
717
|
|
398
|
|
- /* If server did not transition, send back another login
|
399
|
|
- * request without any login strings.
|
400
|
|
- */
|
401
|
|
- if ( ! ( response->flags & ISCSI_LOGIN_FLAG_TRANSITION ) ) {
|
402
|
|
- iscsi_start_login ( iscsi, ( response->flags &
|
403
|
|
- ISCSI_LOGIN_STAGE_MASK ), 0 );
|
|
718
|
+ /* Process strings data */
|
|
719
|
+ for ( ; len-- ; data++ ) {
|
|
720
|
+ iscsi_handle_string_byte ( iscsi, * ( ( uint8_t * ) data ) );
|
|
721
|
+ }
|
|
722
|
+ if ( remaining )
|
404
|
723
|
return;
|
|
724
|
+
|
|
725
|
+ /* Handle login transitions */
|
|
726
|
+ if ( response->flags & ISCSI_LOGIN_FLAG_TRANSITION ) {
|
|
727
|
+ switch ( response->flags & ISCSI_LOGIN_NSG_MASK ) {
|
|
728
|
+ case ISCSI_LOGIN_NSG_OPERATIONAL_NEGOTIATION:
|
|
729
|
+ iscsi->status =
|
|
730
|
+ ( ISCSI_STATUS_OPERATIONAL_NEGOTIATION_PHASE |
|
|
731
|
+ ISCSI_STATUS_STRINGS_OPERATIONAL );
|
|
732
|
+ break;
|
|
733
|
+ case ISCSI_LOGIN_NSG_FULL_FEATURE_PHASE:
|
|
734
|
+ iscsi->status = ISCSI_STATUS_FULL_FEATURE_PHASE;
|
|
735
|
+ break;
|
|
736
|
+ default:
|
|
737
|
+ DBG ( "iSCSI %p got invalid response flags %02x\n",
|
|
738
|
+ iscsi, response->flags );
|
|
739
|
+ iscsi_close ( iscsi, -EIO );
|
|
740
|
+ return;
|
|
741
|
+ }
|
405
|
742
|
}
|
406
|
743
|
|
407
|
|
- /* If we are transitioning to the operational phase, send the
|
408
|
|
- * operational phase login request.
|
|
744
|
+ /* Send next login request PDU if we haven't reached the full
|
|
745
|
+ * feature phase yet.
|
409
|
746
|
*/
|
410
|
|
- if ( ( response->flags & ISCSI_LOGIN_NSG_MASK ) ==
|
411
|
|
- ISCSI_LOGIN_NSG_OPERATIONAL_NEGOTIATION ) {
|
412
|
|
- iscsi_start_login ( iscsi, ISCSI_LOGIN_STAGE_OP, 1 );
|
|
747
|
+ if ( ( iscsi->status & ISCSI_STATUS_PHASE_MASK ) !=
|
|
748
|
+ ISCSI_STATUS_FULL_FEATURE_PHASE ) {
|
|
749
|
+ iscsi_start_login ( iscsi );
|
413
|
750
|
return;
|
414
|
751
|
}
|
415
|
752
|
|
416
|
753
|
/* Record TSIH for future reference */
|
417
|
754
|
iscsi->tsih = ntohl ( response->tsih );
|
418
|
755
|
|
419
|
|
- /* Send the SCSI command */
|
|
756
|
+ /* Send the actual SCSI command */
|
420
|
757
|
iscsi_start_command ( iscsi );
|
421
|
758
|
}
|
422
|
759
|
|
|
@@ -492,6 +829,8 @@ static void iscsi_tx_done ( struct iscsi_session *iscsi ) {
|
492
|
829
|
switch ( common->opcode & ISCSI_OPCODE_MASK ) {
|
493
|
830
|
case ISCSI_OPCODE_DATA_OUT:
|
494
|
831
|
iscsi_data_out_done ( iscsi );
|
|
832
|
+ case ISCSI_OPCODE_LOGIN_REQUEST:
|
|
833
|
+ iscsi_login_request_done ( iscsi );
|
495
|
834
|
default:
|
496
|
835
|
/* No action */
|
497
|
836
|
break;
|
|
@@ -629,8 +968,10 @@ static void iscsi_rx_data ( struct iscsi_session *iscsi, void *data,
|
629
|
968
|
iscsi_rx_r2t ( iscsi, data, len, remaining );
|
630
|
969
|
break;
|
631
|
970
|
default:
|
|
971
|
+ if ( remaining )
|
|
972
|
+ return;
|
632
|
973
|
printf ( "Unknown iSCSI opcode %02x\n", response->opcode );
|
633
|
|
- iscsi->status |= ( ISCSI_STATUS_DONE | ISCSI_STATUS_ERR );
|
|
974
|
+ iscsi_done ( iscsi, -EOPNOTSUPP );
|
634
|
975
|
break;
|
635
|
976
|
}
|
636
|
977
|
}
|
|
@@ -752,18 +1093,18 @@ static void iscsi_newdata ( struct tcp_connection *conn, void *data,
|
752
|
1093
|
* @v status Error code, if any
|
753
|
1094
|
*
|
754
|
1095
|
*/
|
755
|
|
-static void iscsi_closed ( struct tcp_connection *conn, int status __unused ) {
|
|
1096
|
+static void iscsi_closed ( struct tcp_connection *conn, int status ) {
|
756
|
1097
|
struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
|
757
|
1098
|
|
758
|
|
- /* Clear connected flag */
|
759
|
|
- iscsi->status &= ~ISCSI_STATUS_CONNECTED;
|
|
1099
|
+ /* Clear session status */
|
|
1100
|
+ iscsi->status = 0;
|
760
|
1101
|
|
761
|
1102
|
/* Retry connection if within the retry limit, otherwise fail */
|
762
|
1103
|
if ( ++iscsi->retry_count <= ISCSI_MAX_RETRIES ) {
|
763
|
1104
|
tcp_connect ( conn );
|
764
|
1105
|
} else {
|
765
|
1106
|
printf ( "iSCSI retry count exceeded\n" );
|
766
|
|
- iscsi->status |= ( ISCSI_STATUS_DONE | ISCSI_STATUS_ERR );
|
|
1107
|
+ iscsi_done ( iscsi, status );
|
767
|
1108
|
}
|
768
|
1109
|
}
|
769
|
1110
|
|
|
@@ -777,7 +1118,8 @@ static void iscsi_connected ( struct tcp_connection *conn ) {
|
777
|
1118
|
struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
|
778
|
1119
|
|
779
|
1120
|
/* Set connected flag and reset retry count */
|
780
|
|
- iscsi->status |= ISCSI_STATUS_CONNECTED;
|
|
1121
|
+ iscsi->status = ( ISCSI_STATUS_SECURITY_NEGOTIATION_PHASE |
|
|
1122
|
+ ISCSI_STATUS_STRINGS_SECURITY );
|
781
|
1123
|
iscsi->retry_count = 0;
|
782
|
1124
|
|
783
|
1125
|
/* Prepare to receive PDUs. */
|
|
@@ -788,7 +1130,7 @@ static void iscsi_connected ( struct tcp_connection *conn ) {
|
788
|
1130
|
iscsi->itt++;
|
789
|
1131
|
|
790
|
1132
|
/* Start logging in */
|
791
|
|
- iscsi_start_login ( iscsi, ISCSI_LOGIN_STAGE_SEC, 1 );
|
|
1133
|
+ iscsi_start_login ( iscsi );
|
792
|
1134
|
}
|
793
|
1135
|
|
794
|
1136
|
/** iSCSI TCP operations */
|
|
@@ -805,14 +1147,14 @@ static struct tcp_operations iscsi_tcp_operations = {
|
805
|
1147
|
*
|
806
|
1148
|
* @v iscsi iSCSI session
|
807
|
1149
|
* @v command SCSI command
|
808
|
|
- * @ret rc Return status code
|
|
1150
|
+ * @ret aop Asynchronous operation for this SCSI command
|
809
|
1151
|
*/
|
810
|
|
-int iscsi_issue ( struct iscsi_session *iscsi,
|
811
|
|
- struct scsi_command *command ) {
|
|
1152
|
+struct async_operation * iscsi_issue ( struct iscsi_session *iscsi,
|
|
1153
|
+ struct scsi_command *command ) {
|
|
1154
|
+ assert ( iscsi->command == NULL );
|
812
|
1155
|
iscsi->command = command;
|
813
|
|
- iscsi->status &= ~( ISCSI_STATUS_DONE | ISCSI_STATUS_ERR );
|
814
|
1156
|
|
815
|
|
- if ( iscsi->status & ISCSI_STATUS_CONNECTED ) {
|
|
1157
|
+ if ( iscsi->status ) {
|
816
|
1158
|
iscsi_start_command ( iscsi );
|
817
|
1159
|
tcp_senddata ( &iscsi->tcp );
|
818
|
1160
|
} else {
|
|
@@ -820,11 +1162,5 @@ int iscsi_issue ( struct iscsi_session *iscsi,
|
820
|
1162
|
tcp_connect ( &iscsi->tcp );
|
821
|
1163
|
}
|
822
|
1164
|
|
823
|
|
- while ( ! ( iscsi->status & ISCSI_STATUS_DONE ) ) {
|
824
|
|
- step();
|
825
|
|
- }
|
826
|
|
-
|
827
|
|
- iscsi->command = NULL;
|
828
|
|
-
|
829
|
|
- return ( ( iscsi->status & ISCSI_STATUS_ERR ) ? -EIO : 0 );
|
|
1165
|
+ return &iscsi->aop;
|
830
|
1166
|
}
|