|
@@ -31,6 +31,7 @@
|
31
|
31
|
#include <gpxe/process.h>
|
32
|
32
|
#include <gpxe/uaccess.h>
|
33
|
33
|
#include <gpxe/tcpip.h>
|
|
34
|
+#include <gpxe/dhcp.h>
|
34
|
35
|
#include <gpxe/iscsi.h>
|
35
|
36
|
|
36
|
37
|
/** @file
|
|
@@ -39,6 +40,18 @@
|
39
|
40
|
*
|
40
|
41
|
*/
|
41
|
42
|
|
|
43
|
+/** iSCSI initiator name (explicitly specified) */
|
|
44
|
+char *iscsi_initiator_iqn;
|
|
45
|
+
|
|
46
|
+/** Default iSCSI initiator name (constructed from hostname) */
|
|
47
|
+char *iscsi_default_initiator_iqn;
|
|
48
|
+
|
|
49
|
+/** iSCSI username */
|
|
50
|
+char *iscsi_username;
|
|
51
|
+
|
|
52
|
+/** iSCSI password */
|
|
53
|
+char *iscsi_password;
|
|
54
|
+
|
42
|
55
|
static void iscsi_start_tx ( struct iscsi_session *iscsi );
|
43
|
56
|
static void iscsi_start_login ( struct iscsi_session *iscsi );
|
44
|
57
|
static void iscsi_start_data_out ( struct iscsi_session *iscsi,
|
|
@@ -63,11 +76,8 @@ static void iscsi_free ( struct refcnt *refcnt ) {
|
63
|
76
|
struct iscsi_session *iscsi =
|
64
|
77
|
container_of ( refcnt, struct iscsi_session, refcnt );
|
65
|
78
|
|
66
|
|
- free ( iscsi->initiator_iqn );
|
67
|
79
|
free ( iscsi->target_address );
|
68
|
80
|
free ( iscsi->target_iqn );
|
69
|
|
- free ( iscsi->username );
|
70
|
|
- free ( iscsi->password );
|
71
|
81
|
chap_finish ( &iscsi->chap );
|
72
|
82
|
iscsi_rx_buffered_data_done ( iscsi );
|
73
|
83
|
free ( iscsi );
|
|
@@ -89,7 +99,7 @@ static int iscsi_open_connection ( struct iscsi_session *iscsi ) {
|
89
|
99
|
|
90
|
100
|
/* Open socket */
|
91
|
101
|
memset ( &target, 0, sizeof ( target ) );
|
92
|
|
- target.st_port = htons ( ISCSI_PORT );
|
|
102
|
+ target.st_port = htons ( iscsi->target_port );
|
93
|
103
|
if ( ( rc = xfer_open_named_socket ( &iscsi->socket, SOCK_STREAM,
|
94
|
104
|
( struct sockaddr * ) &target,
|
95
|
105
|
iscsi->target_address,
|
|
@@ -428,16 +438,22 @@ static int iscsi_tx_data_out ( struct iscsi_session *iscsi ) {
|
428
|
438
|
*/
|
429
|
439
|
static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi,
|
430
|
440
|
void *data, size_t len ) {
|
|
441
|
+ char *initiator_iqn;
|
431
|
442
|
unsigned int used = 0;
|
432
|
443
|
unsigned int i;
|
433
|
444
|
|
434
|
445
|
if ( iscsi->status & ISCSI_STATUS_STRINGS_SECURITY ) {
|
|
446
|
+ initiator_iqn = iscsi_initiator_iqn;
|
|
447
|
+ if ( ! initiator_iqn )
|
|
448
|
+ initiator_iqn = iscsi_default_initiator_iqn;
|
|
449
|
+ if ( ! initiator_iqn )
|
|
450
|
+ initiator_iqn = "iqn.2000-09.org.etherboot:UNKNOWN";
|
435
|
451
|
used += ssnprintf ( data + used, len - used,
|
436
|
452
|
"InitiatorName=%s%c"
|
437
|
453
|
"TargetName=%s%c"
|
438
|
454
|
"SessionType=Normal%c"
|
439
|
455
|
"AuthMethod=CHAP,None%c",
|
440
|
|
- iscsi->initiator_iqn, 0,
|
|
456
|
+ initiator_iqn, 0,
|
441
|
457
|
iscsi->target_iqn, 0, 0, 0 );
|
442
|
458
|
}
|
443
|
459
|
|
|
@@ -446,10 +462,10 @@ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi,
|
446
|
462
|
}
|
447
|
463
|
|
448
|
464
|
if ( ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_RESPONSE ) &&
|
449
|
|
- iscsi->username ) {
|
|
465
|
+ iscsi_username ) {
|
450
|
466
|
used += ssnprintf ( data + used, len - used,
|
451
|
467
|
"CHAP_N=%s%cCHAP_R=0x",
|
452
|
|
- iscsi->username, 0 );
|
|
468
|
+ iscsi_username, 0 );
|
453
|
469
|
for ( i = 0 ; i < iscsi->chap.response_len ; i++ ) {
|
454
|
470
|
used += ssnprintf ( data + used, len - used, "%02x",
|
455
|
471
|
iscsi->chap.response[i] );
|
|
@@ -633,9 +649,9 @@ static int iscsi_handle_chap_i_value ( struct iscsi_session *iscsi,
|
633
|
649
|
* challenge.
|
634
|
650
|
*/
|
635
|
651
|
chap_set_identifier ( &iscsi->chap, identifier );
|
636
|
|
- if ( iscsi->password ) {
|
637
|
|
- chap_update ( &iscsi->chap, iscsi->password,
|
638
|
|
- strlen ( iscsi->password ) );
|
|
652
|
+ if ( iscsi_password ) {
|
|
653
|
+ chap_update ( &iscsi->chap, iscsi_password,
|
|
654
|
+ strlen ( iscsi_password ) );
|
639
|
655
|
}
|
640
|
656
|
|
641
|
657
|
return 0;
|
|
@@ -1245,55 +1261,275 @@ static struct xfer_interface_operations iscsi_socket_operations = {
|
1245
|
1261
|
.deliver_raw = iscsi_socket_deliver_raw,
|
1246
|
1262
|
};
|
1247
|
1263
|
|
|
1264
|
+
|
|
1265
|
+/****************************************************************************
|
|
1266
|
+ *
|
|
1267
|
+ * iSCSI to SCSI interface
|
|
1268
|
+ *
|
|
1269
|
+ */
|
|
1270
|
+
|
1248
|
1271
|
/**
|
1249
|
|
- * Issue SCSI command via iSCSI session
|
|
1272
|
+ * Issue SCSI command
|
1250
|
1273
|
*
|
1251
|
|
- * @v iscsi iSCSI session
|
|
1274
|
+ * @v scsi SCSI interface
|
1252
|
1275
|
* @v command SCSI command
|
1253
|
|
- * @v parent Parent asynchronous operation
|
1254
|
1276
|
* @ret rc Return status code
|
1255
|
1277
|
*/
|
1256
|
|
-int iscsi_issue ( struct iscsi_session *iscsi, struct scsi_command *command,
|
1257
|
|
- struct async *parent ) {
|
|
1278
|
+static int iscsi_scsi_issue ( struct scsi_interface *scsi,
|
|
1279
|
+ struct scsi_command *command ) {
|
|
1280
|
+ struct iscsi_session *iscsi =
|
|
1281
|
+ container_of ( scsi, struct iscsi_session, scsi );
|
1258
|
1282
|
int rc;
|
1259
|
1283
|
|
1260
|
|
- assert ( iscsi->command == NULL );
|
1261
|
|
- iscsi->command = command;
|
1262
|
1284
|
|
1263
|
|
- if ( iscsi->instant_rc ) {
|
1264
|
|
- /* Abort immediately rather than retrying */
|
|
1285
|
+ /* Abort immediately if we have a recorded permanent failure */
|
|
1286
|
+ if ( iscsi->instant_rc )
|
1265
|
1287
|
return iscsi->instant_rc;
|
1266
|
|
- } else if ( iscsi->status ) {
|
1267
|
|
- /* Session already open: issue command */
|
|
1288
|
+
|
|
1289
|
+ /* Issue command or open connection as appropriate */
|
|
1290
|
+ if ( iscsi->status ) {
|
1268
|
1291
|
iscsi_start_command ( iscsi );
|
1269
|
|
- stream_kick ( &iscsi->stream );
|
1270
|
1292
|
} else {
|
1271
|
|
- /* Session not open: initiate login */
|
1272
|
|
- iscsi->stream.op = &iscsi_stream_operations;
|
1273
|
|
- if ( ( rc = tcp_open ( &iscsi->stream ) ) != 0 ) {
|
1274
|
|
- DBGC ( iscsi, "iSCSI %p could not open stream: %s\n ",
|
1275
|
|
- iscsi, strerror ( rc ) );
|
|
1293
|
+ if ( ( rc = iscsi_open_connection ( iscsi ) ) != 0 )
|
1276
|
1294
|
return rc;
|
|
1295
|
+ }
|
|
1296
|
+
|
|
1297
|
+ return 0;
|
|
1298
|
+}
|
|
1299
|
+
|
|
1300
|
+/**
|
|
1301
|
+ * Detach SCSI interface
|
|
1302
|
+ *
|
|
1303
|
+ * @v scsi SCSI interface
|
|
1304
|
+ * @v rc Reason for close
|
|
1305
|
+ */
|
|
1306
|
+static void iscsi_scsi_detach ( struct scsi_interface *scsi, int rc ) {
|
|
1307
|
+ struct iscsi_session *iscsi =
|
|
1308
|
+ container_of ( scsi, struct iscsi_session, scsi );
|
|
1309
|
+
|
|
1310
|
+ iscsi_close_connection ( iscsi, rc );
|
|
1311
|
+ process_del ( &iscsi->process );
|
|
1312
|
+}
|
|
1313
|
+
|
|
1314
|
+/****************************************************************************
|
|
1315
|
+ *
|
|
1316
|
+ * Instantiator
|
|
1317
|
+ *
|
|
1318
|
+ */
|
|
1319
|
+
|
|
1320
|
+/** iSCSI root path components (as per RFC4173) */
|
|
1321
|
+enum iscsi_root_path_component {
|
|
1322
|
+ RP_LITERAL = 0,
|
|
1323
|
+ RP_SERVERNAME,
|
|
1324
|
+ RP_PROTOCOL,
|
|
1325
|
+ RP_PORT,
|
|
1326
|
+ RP_LUN,
|
|
1327
|
+ RP_TARGETNAME,
|
|
1328
|
+ NUM_RP_COMPONENTS
|
|
1329
|
+};
|
|
1330
|
+
|
|
1331
|
+/**
|
|
1332
|
+ * Parse iSCSI LUN
|
|
1333
|
+ *
|
|
1334
|
+ * @v iscsi iSCSI session
|
|
1335
|
+ * @v lun_string LUN string representation (as per RFC4173)
|
|
1336
|
+ * @ret rc Return status code
|
|
1337
|
+ */
|
|
1338
|
+static int iscsi_parse_lun ( struct iscsi_session *iscsi,
|
|
1339
|
+ const char *lun_string ) {
|
|
1340
|
+ char *p = ( char * ) lun_string;
|
|
1341
|
+ union {
|
|
1342
|
+ uint64_t u64;
|
|
1343
|
+ uint16_t u16[4];
|
|
1344
|
+ } lun;
|
|
1345
|
+ int i;
|
|
1346
|
+
|
|
1347
|
+ for ( i = 0 ; i < 4 ; i++ ) {
|
|
1348
|
+ lun.u16[i] = strtoul ( p, &p, 16 );
|
|
1349
|
+ if ( *p != '-' )
|
|
1350
|
+ return -EINVAL;
|
|
1351
|
+ p++;
|
|
1352
|
+ }
|
|
1353
|
+ if ( *p )
|
|
1354
|
+ return -EINVAL;
|
|
1355
|
+
|
|
1356
|
+ iscsi->lun = lun.u64;
|
|
1357
|
+ return 0;
|
|
1358
|
+}
|
|
1359
|
+
|
|
1360
|
+/**
|
|
1361
|
+ * Parse iSCSI root path
|
|
1362
|
+ *
|
|
1363
|
+ * @v iscsi iSCSI session
|
|
1364
|
+ * @v root_path iSCSI root path (as per RFC4173)
|
|
1365
|
+ * @ret rc Return status code
|
|
1366
|
+ */
|
|
1367
|
+static int iscsi_parse_root_path ( struct iscsi_session *iscsi,
|
|
1368
|
+ const char *root_path ) {
|
|
1369
|
+ const char *p = root_path;
|
|
1370
|
+ char *fragment;
|
|
1371
|
+ size_t len;
|
|
1372
|
+ enum iscsi_root_path_component i;
|
|
1373
|
+ int rc;
|
|
1374
|
+
|
|
1375
|
+ for ( i = 0 ; i < NUM_RP_COMPONENTS ; i++ ) {
|
|
1376
|
+ len = strcspn ( p, ":" );
|
|
1377
|
+ fragment = strndup ( p, len );
|
|
1378
|
+ if ( ! fragment ) {
|
|
1379
|
+ DBGC ( iscsi, "iSCSI %p could not duplicate root "
|
|
1380
|
+ "path component at %s\n", iscsi, p );
|
|
1381
|
+ return -ENOMEM;
|
1277
|
1382
|
}
|
1278
|
|
- if ( ( rc = stream_connect ( &iscsi->stream,
|
1279
|
|
- &iscsi->target ) ) != 0 ) {
|
1280
|
|
- DBGC ( iscsi, "iSCSI %p could not connect: %s\n",
|
1281
|
|
- iscsi, strerror ( rc ) );
|
1282
|
|
- return rc;
|
|
1383
|
+ switch ( i ) {
|
|
1384
|
+ case RP_SERVERNAME:
|
|
1385
|
+ iscsi->target_address = fragment;
|
|
1386
|
+ break;
|
|
1387
|
+ case RP_PORT:
|
|
1388
|
+ iscsi->target_port = strtoul ( fragment, NULL, 10 );
|
|
1389
|
+ if ( ! iscsi->target_port )
|
|
1390
|
+ iscsi->target_port = ISCSI_PORT;
|
|
1391
|
+ free ( fragment );
|
|
1392
|
+ break;
|
|
1393
|
+ case RP_LUN:
|
|
1394
|
+ rc = iscsi_parse_lun ( iscsi, fragment );
|
|
1395
|
+ free ( fragment );
|
|
1396
|
+ if ( rc != 0 ) {
|
|
1397
|
+ DBGC ( iscsi, "iSCSI %p invalid LUN %s\n",
|
|
1398
|
+ iscsi, fragment );
|
|
1399
|
+ return rc;
|
|
1400
|
+ }
|
|
1401
|
+ break;
|
|
1402
|
+ case RP_TARGETNAME:
|
|
1403
|
+ iscsi->target_iqn = fragment;
|
|
1404
|
+ break;
|
|
1405
|
+ default:
|
|
1406
|
+ free ( fragment );
|
|
1407
|
+ break;
|
1283
|
1408
|
}
|
|
1409
|
+ p += len;
|
1284
|
1410
|
}
|
1285
|
1411
|
|
1286
|
|
- async_init ( &iscsi->async, &default_async_operations, parent );
|
1287
|
1412
|
return 0;
|
1288
|
1413
|
}
|
1289
|
1414
|
|
1290
|
1415
|
/**
|
1291
|
|
- * Close down iSCSI session
|
|
1416
|
+ * Attach iSCSI interface
|
1292
|
1417
|
*
|
1293
|
|
- * @v iscsi iSCSI session
|
1294
|
|
- * @ret aop Asynchronous operation
|
|
1418
|
+ * @v scsi SCSI interface
|
|
1419
|
+ * @v root_path iSCSI root path (as per RFC4173)
|
|
1420
|
+ * @ret rc Return status code
|
1295
|
1421
|
*/
|
1296
|
|
-void iscsi_shutdown ( struct iscsi_session *iscsi ) {
|
1297
|
|
- iscsi_close_connection ( iscsi, 0 );
|
|
1422
|
+int iscsi_attach ( struct scsi_interface *scsi, const char *root_path ) {
|
|
1423
|
+ struct iscsi_session *iscsi;
|
|
1424
|
+ int rc;
|
|
1425
|
+
|
|
1426
|
+ /* Allocate and initialise structure */
|
|
1427
|
+ iscsi = zalloc ( sizeof ( *iscsi ) );
|
|
1428
|
+ if ( ! iscsi )
|
|
1429
|
+ return -ENOMEM;
|
|
1430
|
+ xfer_init ( &iscsi->socket, &iscsi_socket_operations, &iscsi->refcnt );
|
|
1431
|
+ process_init ( &iscsi->process, iscsi_tx_step, &iscsi->refcnt );
|
|
1432
|
+
|
|
1433
|
+ /* Parse root path */
|
|
1434
|
+ if ( ( rc = iscsi_parse_root_path ( iscsi, root_path ) ) != 0 )
|
|
1435
|
+ goto err;
|
|
1436
|
+
|
|
1437
|
+ /* Sanity checks */
|
|
1438
|
+ if ( ! iscsi->target_address ) {
|
|
1439
|
+ DBGC ( iscsi, "iSCSI %p does not yet support discovery\n",
|
|
1440
|
+ iscsi );
|
|
1441
|
+ rc = -ENOTSUP;
|
|
1442
|
+ goto err;
|
|
1443
|
+ }
|
|
1444
|
+ if ( ! iscsi->target_iqn ) {
|
|
1445
|
+ DBGC ( iscsi, "iSCSI %p no target address supplied in %s\n",
|
|
1446
|
+ iscsi, root_path );
|
|
1447
|
+ rc = -EINVAL;
|
|
1448
|
+ goto err;
|
|
1449
|
+ }
|
|
1450
|
+
|
|
1451
|
+ /* Attach parent interface, mortalise self, and return */
|
|
1452
|
+ scsi_plug_plug ( &iscsi->scsi, scsi );
|
|
1453
|
+ ref_put ( &iscsi->refcnt );
|
|
1454
|
+ return 0;
|
|
1455
|
+
|
|
1456
|
+ err:
|
1298
|
1457
|
ref_put ( &iscsi->refcnt );
|
|
1458
|
+ return rc;
|
|
1459
|
+}
|
|
1460
|
+
|
|
1461
|
+/****************************************************************************
|
|
1462
|
+ *
|
|
1463
|
+ * DHCP option applicators
|
|
1464
|
+ *
|
|
1465
|
+ */
|
|
1466
|
+
|
|
1467
|
+/**
|
|
1468
|
+ * Apply DHCP iSCSI option
|
|
1469
|
+ *
|
|
1470
|
+ * @v tag DHCP option tag
|
|
1471
|
+ * @v option DHCP option
|
|
1472
|
+ * @ret rc Return status code
|
|
1473
|
+ */
|
|
1474
|
+static int apply_dhcp_iscsi_string ( unsigned int tag,
|
|
1475
|
+ struct dhcp_option *option ) {
|
|
1476
|
+ char *prefix = "";
|
|
1477
|
+ size_t prefix_len;
|
|
1478
|
+ size_t len;
|
|
1479
|
+ char **string;
|
|
1480
|
+ char *p;
|
|
1481
|
+
|
|
1482
|
+ /* Identify string and prefix */
|
|
1483
|
+ switch ( tag ) {
|
|
1484
|
+ case DHCP_ISCSI_INITIATOR_IQN:
|
|
1485
|
+ string = &iscsi_initiator_iqn;
|
|
1486
|
+ break;
|
|
1487
|
+ case DHCP_EB_USERNAME:
|
|
1488
|
+ string = &iscsi_username;
|
|
1489
|
+ break;
|
|
1490
|
+ case DHCP_EB_PASSWORD:
|
|
1491
|
+ string = &iscsi_password;
|
|
1492
|
+ break;
|
|
1493
|
+ case DHCP_HOST_NAME:
|
|
1494
|
+ string = &iscsi_default_initiator_iqn;
|
|
1495
|
+ prefix = "iqn.2000-09.org.etherboot:";
|
|
1496
|
+ break;
|
|
1497
|
+ default:
|
|
1498
|
+ assert ( 0 );
|
|
1499
|
+ return -EINVAL;
|
|
1500
|
+ }
|
|
1501
|
+
|
|
1502
|
+ /* Free old string */
|
|
1503
|
+ free ( *string );
|
|
1504
|
+ *string = NULL;
|
|
1505
|
+
|
|
1506
|
+ /* Allocate and fill new string */
|
|
1507
|
+ prefix_len = strlen ( prefix );
|
|
1508
|
+ len = ( prefix_len + option->len + 1 );
|
|
1509
|
+ p = *string = malloc ( len );
|
|
1510
|
+ if ( ! p )
|
|
1511
|
+ return -ENOMEM;
|
|
1512
|
+ strcpy ( p, prefix );
|
|
1513
|
+ dhcp_snprintf ( ( p + prefix_len ), ( len - prefix_len ), option );
|
|
1514
|
+ return 0;
|
1299
|
1515
|
}
|
|
1516
|
+
|
|
1517
|
+/** DHCP iSCSI option applicators */
|
|
1518
|
+struct dhcp_option_applicator dhcp_iscsi_applicators[] __dhcp_applicator = {
|
|
1519
|
+ {
|
|
1520
|
+ .tag = DHCP_ISCSI_INITIATOR_IQN,
|
|
1521
|
+ .apply = apply_dhcp_iscsi_string,
|
|
1522
|
+ },
|
|
1523
|
+ {
|
|
1524
|
+ .tag = DHCP_EB_USERNAME,
|
|
1525
|
+ .apply = apply_dhcp_iscsi_string,
|
|
1526
|
+ },
|
|
1527
|
+ {
|
|
1528
|
+ .tag = DHCP_EB_PASSWORD,
|
|
1529
|
+ .apply = apply_dhcp_iscsi_string,
|
|
1530
|
+ },
|
|
1531
|
+ {
|
|
1532
|
+ .tag = DHCP_HOST_NAME,
|
|
1533
|
+ .apply = apply_dhcp_iscsi_string,
|
|
1534
|
+ },
|
|
1535
|
+};
|