ISCSIPDU.cs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. /* Copyright (C) 2012-2016 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 bool ImmediateDelivery; // I-Bit - first byte
  16. public ISCSIOpCodeName OpCode; // first byte
  17. public bool Final; // F-Bit - first byte
  18. public byte[] OpCodeSpecificHeader = new byte[3]; // Final bit is removed!
  19. public byte TotalAHSLength;
  20. public uint DataSegmentLength; // 3 bytes
  21. public byte[] LUNOrOpCodeSpecific = new byte[8];
  22. public uint InitiatorTaskTag; // 4 bytes
  23. public byte[] OpCodeSpecific = new byte[28]; // 28 bytes
  24. public byte[] Data = new byte[0];
  25. protected ISCSIPDU()
  26. {
  27. }
  28. protected ISCSIPDU(byte[] buffer)
  29. {
  30. ImmediateDelivery = (buffer[0] & 0x40) != 0;
  31. OpCode = (ISCSIOpCodeName)(buffer[0] & 0x3F);
  32. Final = (buffer[1] & 0x80) != 0;
  33. Array.Copy(buffer, 1, OpCodeSpecificHeader, 0, 3);
  34. OpCodeSpecificHeader[0] &= 0x7F; // remove the final bit
  35. TotalAHSLength = buffer[4];
  36. DataSegmentLength = (uint)(buffer[5] << 16 | buffer[6] << 8 | buffer[7]);
  37. Array.Copy(buffer, 8, LUNOrOpCodeSpecific, 0, 8);
  38. InitiatorTaskTag = BigEndianConverter.ToUInt32(buffer, 16);
  39. Array.Copy(buffer, 20, OpCodeSpecific, 0, 28);
  40. Data = new byte[DataSegmentLength];
  41. Array.Copy(buffer, 48, Data, 0, DataSegmentLength);
  42. }
  43. virtual public byte[] GetBytes()
  44. {
  45. DataSegmentLength = (uint)Data.Length; // We must update DataSegmentLength for all subsequest length calculations
  46. byte[] buffer = new byte[this.Length]; // include padding bytes
  47. buffer[0x00] = (byte)OpCode;
  48. if (ImmediateDelivery)
  49. {
  50. buffer[0] |= 0x40;
  51. }
  52. Array.Copy(OpCodeSpecificHeader, 0, buffer, 1, 3);
  53. if (Final) // Note: Login request / response use the the Final bit as the Transmit bit
  54. {
  55. buffer[1] |= 0x80;
  56. }
  57. buffer[4] = TotalAHSLength;
  58. buffer[5] = (byte)((DataSegmentLength >> 16) & 0xFF);
  59. buffer[6] = (byte)((DataSegmentLength >> 8) & 0xFF);
  60. buffer[7] = (byte)((DataSegmentLength >> 0) & 0xFF);
  61. Array.Copy(LUNOrOpCodeSpecific, 0, buffer, 8, 8);
  62. BigEndianWriter.WriteUInt32(buffer, 16, InitiatorTaskTag);
  63. Array.Copy(OpCodeSpecific, 0, buffer, 20, 28);
  64. Array.Copy(Data, 0, buffer, 48, DataSegmentLength);
  65. return buffer;
  66. }
  67. public int Length
  68. {
  69. get
  70. {
  71. int length = (int)(TotalAHSLength + DataSegmentLength + 48);
  72. length = (int)Math.Ceiling((double)length / 4) * 4; // iSCSIPDUs are padded to the closest integer number of four byte words.
  73. return length;
  74. }
  75. }
  76. public static ISCSIPDU GetPDU(byte[] buffer)
  77. {
  78. byte opCode = (byte)(buffer[0x00] & 0x3F);
  79. switch ((ISCSIOpCodeName)opCode)
  80. {
  81. case ISCSIOpCodeName.NOPOut:
  82. return new NOPOutPDU(buffer);
  83. case ISCSIOpCodeName.SCSICommand:
  84. return new SCSICommandPDU(buffer);
  85. case ISCSIOpCodeName.LoginRequest:
  86. return new LoginRequestPDU(buffer);
  87. case ISCSIOpCodeName.TextRequest:
  88. return new TextRequestPDU(buffer);
  89. case ISCSIOpCodeName.SCSIDataOut:
  90. return new SCSIDataOutPDU(buffer);
  91. case ISCSIOpCodeName.LogoutRequest:
  92. return new LogoutRequestPDU(buffer);
  93. case ISCSIOpCodeName.NOPIn:
  94. return new NOPInPDU(buffer);
  95. case ISCSIOpCodeName.SCSIResponse:
  96. return new SCSIResponsePDU(buffer);
  97. case ISCSIOpCodeName.LoginResponse:
  98. return new LoginResponsePDU(buffer);
  99. case ISCSIOpCodeName.TextResponse:
  100. return new TextResponsePDU(buffer);
  101. case ISCSIOpCodeName.SCSIDataIn:
  102. return new SCSIDataInPDU(buffer);
  103. case ISCSIOpCodeName.LogoutResponse:
  104. return new LogoutResponsePDU(buffer);
  105. case ISCSIOpCodeName.ReadyToTransfer:
  106. return new ReadyToTransferPDU(buffer);
  107. default:
  108. return new ISCSIPDU(buffer);
  109. }
  110. }
  111. public static int GetPDULength(byte[] buffer)
  112. {
  113. return GetPDULength(buffer, 0);
  114. }
  115. public static int GetPDULength(byte[] buffer, int offset)
  116. {
  117. byte totalAHSLength = buffer[offset + 4];
  118. int dataSegmentLength = buffer[offset + 5] << 16 | buffer[offset + 6] << 8 | buffer[offset + 7];
  119. // Basic Header segment size is 48 bytes
  120. int length = totalAHSLength + dataSegmentLength + 48;
  121. length = (int)Math.Ceiling((double)length / 4) * 4; // iSCSIPDUs are padded to the closest integer number of four byte words.
  122. return length;
  123. }
  124. }
  125. }