Bladeren bron

Updated to use asynchronous operation model for iSCSI requests

Added CHAP authentication
tags/v0.9.3
Michael Brown 18 jaren geleden
bovenliggende
commit
3f0b4d9ed8
5 gewijzigde bestanden met toevoegingen van 484 en 94 verwijderingen
  1. 1
    1
      src/drivers/scsi/iscsidev.c
  2. 62
    15
      src/include/gpxe/iscsi.h
  3. 413
    77
      src/net/tcp/iscsi.c
  4. 4
    1
      src/tests/dhcptest.c
  5. 4
    0
      src/tests/iscsiboot.c

+ 1
- 1
src/drivers/scsi/iscsidev.c Bestand weergeven

@@ -37,7 +37,7 @@ static int iscsi_command ( struct scsi_device *scsi,
37 37
 	struct iscsi_device *iscsidev
38 38
 		= container_of ( scsi, struct iscsi_device, scsi );
39 39
 
40
-	return iscsi_issue ( &iscsidev->iscsi, command );
40
+	return async_wait ( iscsi_issue ( &iscsidev->iscsi, command ) );
41 41
 }
42 42
 
43 43
 /**

+ 62
- 15
src/include/gpxe/iscsi.h Bestand weergeven

@@ -9,7 +9,9 @@
9 9
 
10 10
 #include <stdint.h>
11 11
 #include <gpxe/tcp.h>
12
+#include <gpxe/async.h>
12 13
 #include <gpxe/scsi.h>
14
+#include <gpxe/chap.h>
13 15
 
14 16
 /** Default iSCSI port */
15 17
 #define ISCSI_PORT 3260
@@ -167,13 +169,6 @@ struct iscsi_bhs_login_request {
167 169
 #define ISCSI_LOGIN_NSG_OPERATIONAL_NEGOTIATION 0x01
168 170
 #define ISCSI_LOGIN_NSG_FULL_FEATURE_PHASE 0x03
169 171
 
170
-/* Combined stage values and mask */
171
-#define ISCSI_LOGIN_STAGE_MASK ( ISCSI_LOGIN_CSG_MASK | ISCSI_LOGIN_NSG_MASK )
172
-#define ISCSI_LOGIN_STAGE_SEC ( ISCSI_LOGIN_CSG_SECURITY_NEGOTIATION | \
173
-				ISCSI_LOGIN_NSG_OPERATIONAL_NEGOTIATION )
174
-#define ISCSI_LOGIN_STAGE_OP ( ISCSI_LOGIN_CSG_OPERATIONAL_NEGOTIATION | \
175
-			       ISCSI_LOGIN_NSG_FULL_FEATURE_PHASE )
176
-
177 172
 /** ISID IANA format marker */
178 173
 #define ISCSI_ISID_IANA 0x40000000
179 174
 
@@ -483,6 +478,23 @@ enum iscsi_rx_state {
483 478
 	ISCSI_RX_DATA_PADDING,
484 479
 };
485 480
 
481
+enum iscsi_string_key_value {
482
+	STRING_KEY = 0,
483
+	STRING_VALUE,
484
+};
485
+
486
+/** iSCSI text string processor state */
487
+struct iscsi_string_state {
488
+	/** Text string key */
489
+	char key[16];
490
+	/** Text string value */
491
+	char value[8];
492
+	/** Key/value flag */
493
+	enum iscsi_string_key_value key_value;
494
+	/** Index into current string */
495
+	unsigned int index;
496
+};
497
+
486 498
 /** An iSCSI session */
487 499
 struct iscsi_session {
488 500
 	/** TCP connection for this session */
@@ -493,6 +505,8 @@ struct iscsi_session {
493 505
 	 * constants.
494 506
 	 */
495 507
 	int status;
508
+	/** Asynchronous operation for the current iSCSI operation */
509
+	struct async_operation aop;
496 510
 	/** Retry count
497 511
 	 *
498 512
 	 * Number of times that the connection has been retried.
@@ -507,6 +521,13 @@ struct iscsi_session {
507 521
 	/** Logical Unit Number (LUN) */
508 522
 	uint64_t lun;
509 523
 
524
+	/** Username */
525
+	const char *username;
526
+	/** Password */
527
+	const char *password;
528
+	/** CHAP challenge/response */
529
+	struct chap_challenge chap;
530
+
510 531
 	/** Target session identifying handle
511 532
 	 *
512 533
 	 * This is assigned by the target when we first log in, and
@@ -569,6 +590,8 @@ struct iscsi_session {
569 590
 	enum iscsi_rx_state rx_state;
570 591
 	/** Byte offset within the current RX state */
571 592
 	size_t rx_offset;
593
+	/** State of strings received during login phase */
594
+	struct iscsi_string_state string;
572 595
 
573 596
 	/** Current SCSI command
574 597
 	 *
@@ -577,20 +600,44 @@ struct iscsi_session {
577 600
 	struct scsi_command *command;
578 601
 };
579 602
 
580
-/** Session is currently connected */
581
-#define ISCSI_STATUS_CONNECTED 0x01
603
+/** iSCSI session is currently in the security negotiation phase */
604
+#define ISCSI_STATUS_SECURITY_NEGOTIATION_PHASE		\
605
+	( ISCSI_LOGIN_CSG_SECURITY_NEGOTIATION |	\
606
+	  ISCSI_LOGIN_NSG_OPERATIONAL_NEGOTIATION )
607
+
608
+/** iSCSI session is currently in the operational parameter
609
+ * negotiation phase
610
+ */
611
+#define ISCSI_STATUS_OPERATIONAL_NEGOTIATION_PHASE	\
612
+	( ISCSI_LOGIN_CSG_OPERATIONAL_NEGOTIATION |	\
613
+	  ISCSI_LOGIN_NSG_FULL_FEATURE_PHASE )
614
+
615
+/** iSCSI session is currently in the full feature phase */
616
+#define ISCSI_STATUS_FULL_FEATURE_PHASE	ISCSI_LOGIN_CSG_FULL_FEATURE_PHASE
617
+
618
+/** Mask for all iSCSI session phases */
619
+#define ISCSI_STATUS_PHASE_MASK ( ISCSI_LOGIN_CSG_MASK | ISCSI_LOGIN_NSG_MASK )
620
+
621
+/** iSCSI session needs to send the initial security negotiation strings */
622
+#define ISCSI_STATUS_STRINGS_SECURITY 0x0100
623
+
624
+/** iSCSI session needs to send the CHAP_A string */
625
+#define ISCSI_STATUS_STRINGS_CHAP_ALGORITHM 0x0200
626
+
627
+/** iSCSI session needs to send the CHAP response */
628
+#define ISCSI_STATUS_STRINGS_CHAP_RESPONSE 0x0400
582 629
 
583
-/** Session has completed */
584
-#define ISCSI_STATUS_DONE 0x02
630
+/** iSCSI session needs to send the operational negotiation strings */
631
+#define ISCSI_STATUS_STRINGS_OPERATIONAL 0x0800
585 632
 
586
-/** Session failed */
587
-#define ISCSI_STATUS_ERR 0x04
633
+/** Mask for all iSCSI "needs to send" flags */
634
+#define ISCSI_STATUS_STRINGS_MASK 0xff00
588 635
 
589 636
 /** Maximum number of retries at connecting */
590 637
 #define ISCSI_MAX_RETRIES 2
591 638
 
592
-extern int iscsi_issue ( struct iscsi_session *iscsi,
593
-			 struct scsi_command *command );
639
+extern struct async_operation * iscsi_issue ( struct iscsi_session *iscsi,
640
+					      struct scsi_command *command );
594 641
 
595 642
 /** An iSCSI device */
596 643
 struct iscsi_device {

+ 413
- 77
src/net/tcp/iscsi.c Bestand weergeven

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

+ 4
- 1
src/tests/dhcptest.c Bestand weergeven

@@ -16,6 +16,8 @@ static int test_dhcp_aoe_boot ( struct net_device *netdev,
16 16
 
17 17
 static int test_dhcp_iscsi_boot ( struct net_device *netdev, char *iscsiname ) {
18 18
 	char *initiator_iqn = "iqn.1900-01.localdomain.localhost:initiator";
19
+	char *username = "joe";
20
+	char *password = "secret";
19 21
 	char *target_iqn;
20 22
 	union {
21 23
 		struct sockaddr_in sin;
@@ -33,7 +35,8 @@ static int test_dhcp_iscsi_boot ( struct net_device *netdev, char *iscsiname ) {
33 35
 	}
34 36
 	inet_aton ( iscsiname, &target.sin.sin_addr );
35 37
 
36
-	return test_iscsiboot ( initiator_iqn, &target.st, target_iqn, netdev );
38
+	return test_iscsiboot ( initiator_iqn, &target.st, target_iqn,
39
+				username, password, netdev );
37 40
 }
38 41
 
39 42
 static int test_dhcp_hello ( char *helloname ) {

+ 4
- 0
src/tests/iscsiboot.c Bestand weergeven

@@ -13,6 +13,8 @@ static struct iscsi_device test_iscsidev;
13 13
 int test_iscsiboot ( const char *initiator_iqn,
14 14
 		     struct sockaddr_tcpip *target,
15 15
 		     const char *target_iqn,
16
+		     const char *username,
17
+		     const char *password,
16 18
 		     struct net_device *netdev ) {
17 19
 	struct int13_drive drive;
18 20
 	int rc;
@@ -22,6 +24,8 @@ int test_iscsiboot ( const char *initiator_iqn,
22 24
 		 sizeof ( test_iscsidev.iscsi.tcp.peer ) );
23 25
 	test_iscsidev.iscsi.initiator = initiator_iqn;
24 26
 	test_iscsidev.iscsi.target = target_iqn;
27
+	test_iscsidev.iscsi.username = username;
28
+	test_iscsidev.iscsi.password = password;
25 29
 
26 30
 	printf ( "Initialising %s\n", target_iqn );
27 31
 	if ( ( rc = init_iscsidev ( &test_iscsidev ) ) != 0 ) {

Laden…
Annuleren
Opslaan