Browse Source

[iscsi] Accept NOP-In PDUs sent by the target

Some iSCSI targets (observed with a Synology DS207+ NAS) send
unsolicited NOP-Ins to the initiator.  RFC 3720 is remarkably unclear
and possibly self-contradictory on how NOPs are supposed to work, but
it seems as though we can legitimately just ignore any unsolicited
NOP-In PDU.

Reported-by: Marc Lecuyer <marc@maxiscreen.com>
Originally-implemented-by: Thomas Miletich <thomas.miletich@gmail.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 13 years ago
parent
commit
711df439df
2 changed files with 84 additions and 0 deletions
  1. 34
    0
      src/include/ipxe/iscsi.h
  2. 50
    0
      src/net/tcp/iscsi.c

+ 34
- 0
src/include/ipxe/iscsi.h View File

93
 /** iSCSI tag magic marker */
93
 /** iSCSI tag magic marker */
94
 #define ISCSI_TAG_MAGIC 0x18ae0000
94
 #define ISCSI_TAG_MAGIC 0x18ae0000
95
 
95
 
96
+/** iSCSI reserved tag value */
97
+#define ISCSI_TAG_RESERVED 0xffffffff
98
+
96
 /**
99
 /**
97
  * iSCSI basic header segment common request fields
100
  * iSCSI basic header segment common request fields
98
  *
101
  *
455
 /** R2T opcode */
458
 /** R2T opcode */
456
 #define ISCSI_OPCODE_R2T 0x31
459
 #define ISCSI_OPCODE_R2T 0x31
457
 
460
 
461
+/**
462
+ * iSCSI NOP-In basic header segment
463
+ *
464
+ */
465
+struct iscsi_nop_in {
466
+	/** Opcode */
467
+	uint8_t opcode;
468
+	/** Reserved */
469
+	uint8_t reserved_a[3];
470
+	/** Segment lengths */
471
+	union iscsi_segment_lengths lengths;
472
+	/** Logical Unit Number */
473
+	struct scsi_lun lun;
474
+	/** Initiator Task Tag */
475
+	uint32_t itt;
476
+	/** Target Transfer Tag */
477
+	uint32_t ttt;
478
+	/** Status sequence number */
479
+	uint32_t statsn;
480
+	/** Expected command sequence number */
481
+	uint32_t expcmdsn;
482
+	/** Maximum command sequence number */
483
+	uint32_t maxcmdsn;
484
+	/** Reserved */
485
+	uint8_t reserved_b[12];
486
+};
487
+
488
+/** NOP-In opcode */
489
+#define ISCSI_OPCODE_NOP_IN 0x20
490
+
458
 /**
491
 /**
459
  * An iSCSI basic header segment
492
  * An iSCSI basic header segment
460
  */
493
  */
468
 	struct iscsi_bhs_data_in data_in;
501
 	struct iscsi_bhs_data_in data_in;
469
 	struct iscsi_bhs_data_out data_out;
502
 	struct iscsi_bhs_data_out data_out;
470
 	struct iscsi_bhs_r2t r2t;
503
 	struct iscsi_bhs_r2t r2t;
504
+	struct iscsi_nop_in nop_in;
471
 	unsigned char bytes[ sizeof ( struct iscsi_bhs_common ) ];
505
 	unsigned char bytes[ sizeof ( struct iscsi_bhs_common ) ];
472
 };
506
 };
473
 
507
 

+ 50
- 0
src/net/tcp/iscsi.c View File

123
 	__einfo_error ( EINFO_EPROTO_INVALID_CHAP_RESPONSE )
123
 	__einfo_error ( EINFO_EPROTO_INVALID_CHAP_RESPONSE )
124
 #define EINFO_EPROTO_INVALID_CHAP_RESPONSE \
124
 #define EINFO_EPROTO_INVALID_CHAP_RESPONSE \
125
 	__einfo_uniqify ( EINFO_EPROTO, 0x04, "Invalid CHAP response" )
125
 	__einfo_uniqify ( EINFO_EPROTO, 0x04, "Invalid CHAP response" )
126
+#define EPROTO_INVALID_NOP_IN \
127
+	__einfo_error ( EINFO_EPROTO_INVALID_NOP_IN )
128
+#define EINFO_EPROTO_INVALID_NOP_IN \
129
+	__einfo_uniqify ( EINFO_EPROTO, 0x05, "Invalid NOP-In received" )
126
 
130
 
127
 /** iSCSI initiator name (explicitly specified) */
131
 /** iSCSI initiator name (explicitly specified) */
128
 static char *iscsi_explicit_initiator_iqn;
132
 static char *iscsi_explicit_initiator_iqn;
589
 	return xfer_deliver_iob ( &iscsi->socket, iobuf );
593
 	return xfer_deliver_iob ( &iscsi->socket, iobuf );
590
 }
594
 }
591
 
595
 
596
+/**
597
+ * Receive data segment of an iSCSI NOP-In
598
+ *
599
+ * @v iscsi		iSCSI session
600
+ * @v data		Received data
601
+ * @v len		Length of received data
602
+ * @v remaining		Data remaining after this data
603
+ * @ret rc		Return status code
604
+ */
605
+static int iscsi_rx_nop_in ( struct iscsi_session *iscsi,
606
+			     const void *data __unused, size_t len __unused,
607
+			     size_t remaining __unused ) {
608
+	struct iscsi_nop_in *nop_in = &iscsi->rx_bhs.nop_in;
609
+
610
+	DBGC2 ( iscsi, "iSCSI %p received NOP-In\n", iscsi );
611
+
612
+	/* RFC 3720 section 10.19 states that "when a target sends a
613
+	 * NOP-In that is not a response to a Nop-Out received from
614
+	 * the initiator, the Initiator Task Tag MUST be set to
615
+	 * 0xffffffff", and section 10.18 states that "upon receipt of
616
+	 * a NOP-In with the Target Transfer Tag set to a valid value
617
+	 * (not the reserved 0xffffffff), the initiator MUST respond
618
+	 * with a NOP-Out".  Since we never send unsolicited NOP-Outs,
619
+	 * my reading of this is that we can handle all permitted
620
+	 * NOP-Ins (which must have ITT set to 0xffffffff) by simply
621
+	 * ignoring them.
622
+	 *
623
+	 * There is some ambiguity in the RFC, since there are other
624
+	 * places that suggest that a target is supposed to be able to
625
+	 * send an unsolicited NOP-In and expect a NOP-Out response.
626
+	 * We catch any apparent attempts to use this immediately, so
627
+	 * that the relevant error gets reported to the iPXE user,
628
+	 * rather than just having the target drop the connection when
629
+	 * it times out waiting for the NOP-Out response.
630
+	 */
631
+	if ( nop_in->itt != htonl ( ISCSI_TAG_RESERVED ) ) {
632
+		DBGC ( iscsi, "iSCSI %p received invalid NOP-In with ITT "
633
+		       "%08x\n", iscsi, ntohl ( nop_in->itt ) );
634
+		return -EPROTO_INVALID_NOP_IN;
635
+	}
636
+
637
+	return 0;
638
+}
639
+
592
 /****************************************************************************
640
 /****************************************************************************
593
  *
641
  *
594
  * iSCSI login
642
  * iSCSI login
1539
 		return iscsi_rx_data_in ( iscsi, data, len, remaining );
1587
 		return iscsi_rx_data_in ( iscsi, data, len, remaining );
1540
 	case ISCSI_OPCODE_R2T:
1588
 	case ISCSI_OPCODE_R2T:
1541
 		return iscsi_rx_r2t ( iscsi, data, len, remaining );
1589
 		return iscsi_rx_r2t ( iscsi, data, len, remaining );
1590
+	case ISCSI_OPCODE_NOP_IN:
1591
+		return iscsi_rx_nop_in ( iscsi, data, len, remaining );
1542
 	default:
1592
 	default:
1543
 		if ( remaining )
1593
 		if ( remaining )
1544
 			return 0;
1594
 			return 0;

Loading…
Cancel
Save