Browse Source

Updated to use asynchronous operation model for iSCSI requests

Added CHAP authentication
tags/v0.9.3
Michael Brown 18 years ago
parent
commit
3f0b4d9ed8
5 changed files with 484 additions and 94 deletions
  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 View File

37
 	struct iscsi_device *iscsidev
37
 	struct iscsi_device *iscsidev
38
 		= container_of ( scsi, struct iscsi_device, scsi );
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 View File

9
 
9
 
10
 #include <stdint.h>
10
 #include <stdint.h>
11
 #include <gpxe/tcp.h>
11
 #include <gpxe/tcp.h>
12
+#include <gpxe/async.h>
12
 #include <gpxe/scsi.h>
13
 #include <gpxe/scsi.h>
14
+#include <gpxe/chap.h>
13
 
15
 
14
 /** Default iSCSI port */
16
 /** Default iSCSI port */
15
 #define ISCSI_PORT 3260
17
 #define ISCSI_PORT 3260
167
 #define ISCSI_LOGIN_NSG_OPERATIONAL_NEGOTIATION 0x01
169
 #define ISCSI_LOGIN_NSG_OPERATIONAL_NEGOTIATION 0x01
168
 #define ISCSI_LOGIN_NSG_FULL_FEATURE_PHASE 0x03
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
 /** ISID IANA format marker */
172
 /** ISID IANA format marker */
178
 #define ISCSI_ISID_IANA 0x40000000
173
 #define ISCSI_ISID_IANA 0x40000000
179
 
174
 
483
 	ISCSI_RX_DATA_PADDING,
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
 /** An iSCSI session */
498
 /** An iSCSI session */
487
 struct iscsi_session {
499
 struct iscsi_session {
488
 	/** TCP connection for this session */
500
 	/** TCP connection for this session */
493
 	 * constants.
505
 	 * constants.
494
 	 */
506
 	 */
495
 	int status;
507
 	int status;
508
+	/** Asynchronous operation for the current iSCSI operation */
509
+	struct async_operation aop;
496
 	/** Retry count
510
 	/** Retry count
497
 	 *
511
 	 *
498
 	 * Number of times that the connection has been retried.
512
 	 * Number of times that the connection has been retried.
507
 	/** Logical Unit Number (LUN) */
521
 	/** Logical Unit Number (LUN) */
508
 	uint64_t lun;
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
 	/** Target session identifying handle
531
 	/** Target session identifying handle
511
 	 *
532
 	 *
512
 	 * This is assigned by the target when we first log in, and
533
 	 * This is assigned by the target when we first log in, and
569
 	enum iscsi_rx_state rx_state;
590
 	enum iscsi_rx_state rx_state;
570
 	/** Byte offset within the current RX state */
591
 	/** Byte offset within the current RX state */
571
 	size_t rx_offset;
592
 	size_t rx_offset;
593
+	/** State of strings received during login phase */
594
+	struct iscsi_string_state string;
572
 
595
 
573
 	/** Current SCSI command
596
 	/** Current SCSI command
574
 	 *
597
 	 *
577
 	struct scsi_command *command;
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
 /** Maximum number of retries at connecting */
636
 /** Maximum number of retries at connecting */
590
 #define ISCSI_MAX_RETRIES 2
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
 /** An iSCSI device */
642
 /** An iSCSI device */
596
 struct iscsi_device {
643
 struct iscsi_device {

+ 413
- 77
src/net/tcp/iscsi.c View File

18
 
18
 
19
 #include <stddef.h>
19
 #include <stddef.h>
20
 #include <string.h>
20
 #include <string.h>
21
+#include <stdlib.h>
21
 #include <vsprintf.h>
22
 #include <vsprintf.h>
22
 #include <errno.h>
23
 #include <errno.h>
23
 #include <assert.h>
24
 #include <assert.h>
37
 static void iscsi_start_data_out ( struct iscsi_session *iscsi,
38
 static void iscsi_start_data_out ( struct iscsi_session *iscsi,
38
 				   unsigned int datasn );
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
  * iSCSI SCSI command issuing
83
  * iSCSI SCSI command issuing
112
 	iscsi->command->status = response->status;
153
 	iscsi->command->status = response->status;
113
 
154
 
114
 	/* Mark as completed, with error if applicable */
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
 	if ( ( offset + len ) == iscsi->command->data_in_len ) {
189
 	if ( ( offset + len ) == iscsi->command->data_in_len ) {
147
 		assert ( data_in->flags & ISCSI_FLAG_FINAL );
190
 		assert ( data_in->flags & ISCSI_FLAG_FINAL );
148
 		assert ( remaining == 0 );
191
 		assert ( remaining == 0 );
149
-		iscsi->status |= ISCSI_STATUS_DONE;
192
+		iscsi_done ( iscsi, 0 );
150
 	}
193
 	}
151
 }
194
 }
152
 
195
 
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
  * Build iSCSI login request strings
331
  * Build iSCSI login request strings
263
  *
332
  *
291
  */
360
  */
292
 static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi,
361
 static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi,
293
 					       void *data, size_t len ) {
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
  * Build iSCSI login request BHS
409
  * Build iSCSI login request BHS
326
  *
410
  *
327
  * @v iscsi		iSCSI session
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
 	struct iscsi_bhs_login_request *request = &iscsi->tx_bhs.login_request;
414
 	struct iscsi_bhs_login_request *request = &iscsi->tx_bhs.login_request;
334
 	int len;
415
 	int len;
335
 
416
 
337
 	iscsi_start_tx ( iscsi );
418
 	iscsi_start_tx ( iscsi );
338
 	request->opcode = ( ISCSI_OPCODE_LOGIN_REQUEST |
419
 	request->opcode = ( ISCSI_OPCODE_LOGIN_REQUEST |
339
 			    ISCSI_FLAG_IMMEDIATE );
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
 	/* version_max and version_min left as zero */
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
 	request->isid_iana_en = htonl ( ISCSI_ISID_IANA |
426
 	request->isid_iana_en = htonl ( ISCSI_ISID_IANA |
348
 					IANA_EN_FEN_SYSTEMS );
427
 					IANA_EN_FEN_SYSTEMS );
349
 	/* isid_iana_qual left as zero */
428
 	/* isid_iana_qual left as zero */
354
 	request->expstatsn = htonl ( iscsi->statsn + 1 );
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
  * Transmit data segment of an iSCSI login request PDU
449
  * Transmit data segment of an iSCSI login request PDU
359
  *
450
  *
370
 		   len - iscsi->tx_offset );
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
  * Receive data segment of an iSCSI login response PDU
697
  * Receive data segment of an iSCSI login response PDU
375
  *
698
  *
379
  * @v remaining		Data remaining after this data
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
 	struct iscsi_bhs_login_response *response
707
 	struct iscsi_bhs_login_response *response
387
 		= &iscsi->rx_bhs.login_response;
708
 		= &iscsi->rx_bhs.login_response;
388
 
709
 
390
 	if ( response->status_class != 0 ) {
711
 	if ( response->status_class != 0 ) {
391
 		printf ( "iSCSI login failure: class %02x detail %02x\n",
712
 		printf ( "iSCSI login failure: class %02x detail %02x\n",
392
 			 response->status_class, response->status_detail );
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
 		return;
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
 		return;
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
 		return;
750
 		return;
414
 	}
751
 	}
415
 
752
 
416
 	/* Record TSIH for future reference */
753
 	/* Record TSIH for future reference */
417
 	iscsi->tsih = ntohl ( response->tsih );
754
 	iscsi->tsih = ntohl ( response->tsih );
418
 	
755
 	
419
-	/* Send the SCSI command */
756
+	/* Send the actual SCSI command */
420
 	iscsi_start_command ( iscsi );
757
 	iscsi_start_command ( iscsi );
421
 }
758
 }
422
 
759
 
492
 	switch ( common->opcode & ISCSI_OPCODE_MASK ) {
829
 	switch ( common->opcode & ISCSI_OPCODE_MASK ) {
493
 	case ISCSI_OPCODE_DATA_OUT:
830
 	case ISCSI_OPCODE_DATA_OUT:
494
 		iscsi_data_out_done ( iscsi );
831
 		iscsi_data_out_done ( iscsi );
832
+	case ISCSI_OPCODE_LOGIN_REQUEST:
833
+		iscsi_login_request_done ( iscsi );
495
 	default:
834
 	default:
496
 		/* No action */
835
 		/* No action */
497
 		break;
836
 		break;
629
 		iscsi_rx_r2t ( iscsi, data, len, remaining );
968
 		iscsi_rx_r2t ( iscsi, data, len, remaining );
630
 		break;
969
 		break;
631
 	default:
970
 	default:
971
+		if ( remaining )
972
+			return;
632
 		printf ( "Unknown iSCSI opcode %02x\n", response->opcode );
973
 		printf ( "Unknown iSCSI opcode %02x\n", response->opcode );
633
-		iscsi->status |= ( ISCSI_STATUS_DONE | ISCSI_STATUS_ERR );
974
+		iscsi_done ( iscsi, -EOPNOTSUPP );
634
 		break;
975
 		break;
635
 	}
976
 	}
636
 }
977
 }
752
  * @v status		Error code, if any
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
 	struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
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
 	/* Retry connection if within the retry limit, otherwise fail */
1102
 	/* Retry connection if within the retry limit, otherwise fail */
762
 	if ( ++iscsi->retry_count <= ISCSI_MAX_RETRIES ) {
1103
 	if ( ++iscsi->retry_count <= ISCSI_MAX_RETRIES ) {
763
 		tcp_connect ( conn );
1104
 		tcp_connect ( conn );
764
 	} else {
1105
 	} else {
765
 		printf ( "iSCSI retry count exceeded\n" );
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
 	struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
1118
 	struct iscsi_session *iscsi = tcp_to_iscsi ( conn );
778
 
1119
 
779
 	/* Set connected flag and reset retry count */
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
 	iscsi->retry_count = 0;
1123
 	iscsi->retry_count = 0;
782
 
1124
 
783
 	/* Prepare to receive PDUs. */
1125
 	/* Prepare to receive PDUs. */
788
 	iscsi->itt++;
1130
 	iscsi->itt++;
789
 
1131
 
790
 	/* Start logging in */
1132
 	/* Start logging in */
791
-	iscsi_start_login ( iscsi, ISCSI_LOGIN_STAGE_SEC, 1 );
1133
+	iscsi_start_login ( iscsi );
792
 }
1134
 }
793
 
1135
 
794
 /** iSCSI TCP operations */
1136
 /** iSCSI TCP operations */
805
  *
1147
  *
806
  * @v iscsi		iSCSI session
1148
  * @v iscsi		iSCSI session
807
  * @v command		SCSI command
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
 	iscsi->command = command;
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
 		iscsi_start_command ( iscsi );
1158
 		iscsi_start_command ( iscsi );
817
 		tcp_senddata ( &iscsi->tcp );
1159
 		tcp_senddata ( &iscsi->tcp );
818
 	} else {
1160
 	} else {
820
 		tcp_connect ( &iscsi->tcp );
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 View File

16
 
16
 
17
 static int test_dhcp_iscsi_boot ( struct net_device *netdev, char *iscsiname ) {
17
 static int test_dhcp_iscsi_boot ( struct net_device *netdev, char *iscsiname ) {
18
 	char *initiator_iqn = "iqn.1900-01.localdomain.localhost:initiator";
18
 	char *initiator_iqn = "iqn.1900-01.localdomain.localhost:initiator";
19
+	char *username = "joe";
20
+	char *password = "secret";
19
 	char *target_iqn;
21
 	char *target_iqn;
20
 	union {
22
 	union {
21
 		struct sockaddr_in sin;
23
 		struct sockaddr_in sin;
33
 	}
35
 	}
34
 	inet_aton ( iscsiname, &target.sin.sin_addr );
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
 static int test_dhcp_hello ( char *helloname ) {
42
 static int test_dhcp_hello ( char *helloname ) {

+ 4
- 0
src/tests/iscsiboot.c View File

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

Loading…
Cancel
Save