ISCSIPDU.cs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. /* Copyright (C) 2012-2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
  2. *
  3. * You can redistribute this program and/or modify it under the terms of
  4. * the GNU Lesser Public License as published by the Free Software Foundation,
  5. * either version 3 of the License, or (at your option) any later version.
  6. */
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Text;
  10. using Utilities;
  11. namespace ISCSI
  12. {
  13. public class ISCSIPDU
  14. {
  15. public const int BasicHeaderSegmentLength = 48;
  16. public bool ImmediateDelivery; // I-Bit - first byte
  17. public ISCSIOpCodeName OpCode; // first byte
  18. public bool Final; // F-Bit - first byte
  19. public byte[] OpCodeSpecificHeader = new byte[3]; // Final bit is removed!
  20. public byte TotalAHSLength;
  21. public uint DataSegmentLength; // 3 bytes
  22. public byte[] LUNOrOpCodeSpecific = new byte[8];
  23. public uint InitiatorTaskTag; // 4 bytes
  24. public byte[] OpCodeSpecific = new byte[28]; // 28 bytes
  25. public byte[] Data = new byte[0];
  26. protected ISCSIPDU()
  27. {
  28. }
  29. protected ISCSIPDU(byte[] buffer, int offset)
  30. {
  31. ImmediateDelivery = (buffer[offset + 0] & 0x40) != 0;
  32. OpCode = (ISCSIOpCodeName)(buffer[offset + 0] & 0x3F);
  33. Final = (buffer[offset + 1] & 0x80) != 0;
  34. Array.Copy(buffer, offset + 1, OpCodeSpecificHeader, 0, 3);
  35. OpCodeSpecificHeader[0] &= 0x7F; // remove the final bit
  36. TotalAHSLength = buffer[offset + 4];
  37. DataSegmentLength = (uint)(buffer[offset + 5] << 16 | buffer[offset + 6] << 8 | buffer[offset + 7]);
  38. Array.Copy(buffer, offset + 8, LUNOrOpCodeSpecific, 0, 8);
  39. InitiatorTaskTag = BigEndianConverter.ToUInt32(buffer, offset + 16);
  40. Array.Copy(buffer, offset + 20, OpCodeSpecific, 0, 28);
  41. Data = new byte[DataSegmentLength];
  42. Array.Copy(buffer, offset + 48, Data, 0, DataSegmentLength);
  43. }
  44. virtual public byte[] GetBytes()
  45. {
  46. DataSegmentLength = (uint)Data.Length; // We must update DataSegmentLength for all subsequest length calculations
  47. byte[] buffer = new byte[this.Length]; // include padding bytes
  48. buffer[0x00] = (byte)OpCode;
  49. if (ImmediateDelivery)
  50. {
  51. buffer[0] |= 0x40;
  52. }
  53. Array.Copy(OpCodeSpecificHeader, 0, buffer, 1, 3);
  54. if (Final) // Note: Login request / response use the the Final bit as the Transmit bit
  55. {
  56. buffer[1] |= 0x80;
  57. }
  58. buffer[4] = TotalAHSLength;
  59. BigEndianWriter.WriteUInt24(buffer, 5, DataSegmentLength);
  60. Array.Copy(LUNOrOpCodeSpecific, 0, buffer, 8, 8);
  61. BigEndianWriter.WriteUInt32(buffer, 16, InitiatorTaskTag);
  62. Array.Copy(OpCodeSpecific, 0, buffer, 20, 28);
  63. Array.Copy(Data, 0, buffer, 48, DataSegmentLength);
  64. return buffer;
  65. }
  66. public int Length
  67. {
  68. get
  69. {
  70. int length = (int)(BasicHeaderSegmentLength + TotalAHSLength + DataSegmentLength);
  71. length = (int)Math.Ceiling((double)length / 4) * 4; // iSCSIPDUs are padded to the closest integer number of four byte words.
  72. return length;
  73. }
  74. }
  75. public static ISCSIPDU GetPDU(byte[] buffer, int offset)
  76. {
  77. byte opCode = (byte)(buffer[offset + 0x00] & 0x3F);
  78. switch ((ISCSIOpCodeName)opCode)
  79. {
  80. case ISCSIOpCodeName.NOPOut:
  81. return new NOPOutPDU(buffer, offset);
  82. case ISCSIOpCodeName.SCSICommand:
  83. return new SCSICommandPDU(buffer, offset);
  84. case ISCSIOpCodeName.LoginRequest:
  85. return new LoginRequestPDU(buffer, offset);
  86. case ISCSIOpCodeName.TextRequest:
  87. return new TextRequestPDU(buffer, offset);
  88. case ISCSIOpCodeName.SCSIDataOut:
  89. return new SCSIDataOutPDU(buffer, offset);
  90. case ISCSIOpCodeName.LogoutRequest:
  91. return new LogoutRequestPDU(buffer, offset);
  92. case ISCSIOpCodeName.NOPIn:
  93. return new NOPInPDU(buffer, offset);
  94. case ISCSIOpCodeName.SCSIResponse:
  95. return new SCSIResponsePDU(buffer, offset);
  96. case ISCSIOpCodeName.LoginResponse:
  97. return new LoginResponsePDU(buffer, offset);
  98. case ISCSIOpCodeName.TextResponse:
  99. return new TextResponsePDU(buffer, offset);
  100. case ISCSIOpCodeName.SCSIDataIn:
  101. return new SCSIDataInPDU(buffer, offset);
  102. case ISCSIOpCodeName.LogoutResponse:
  103. return new LogoutResponsePDU(buffer, offset);
  104. case ISCSIOpCodeName.ReadyToTransfer:
  105. return new ReadyToTransferPDU(buffer, offset);
  106. default:
  107. return new ISCSIPDU(buffer, offset);
  108. }
  109. }
  110. public static int GetPDULength(byte[] buffer)
  111. {
  112. return GetPDULength(buffer, 0);
  113. }
  114. public static int GetPDULength(byte[] buffer, int offset)
  115. {
  116. byte totalAHSLength = buffer[offset + 4];
  117. int dataSegmentLength = buffer[offset + 5] << 16 | buffer[offset + 6] << 8 | buffer[offset + 7];
  118. int length = BasicHeaderSegmentLength + totalAHSLength + dataSegmentLength;
  119. length = (int)Math.Ceiling((double)length / 4) * 4; // iSCSIPDUs are padded to the closest integer number of four byte words.
  120. return length;
  121. }
  122. }
  123. }