ISCSIServer.Login.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  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.Net;
  10. using System.Text;
  11. using Utilities;
  12. namespace ISCSI.Server
  13. {
  14. public partial class ISCSIServer
  15. {
  16. private LoginResponsePDU GetLoginResponsePDU(LoginRequestPDU request, ISCSISession session, ConnectionParameters connection)
  17. {
  18. LoginResponsePDU response = new LoginResponsePDU();
  19. response.Transit = request.Transit;
  20. response.Continue = false;
  21. // The stage codes are:
  22. // 0 - SecurityNegotiation
  23. // 1 - LoginOperationalNegotiation
  24. // 3 - FullFeaturePhase
  25. response.CurrentStage = request.CurrentStage;
  26. response.NextStage = request.NextStage;
  27. response.VersionMax = request.VersionMax;
  28. response.VersionActive = request.VersionMin;
  29. response.ISID = request.ISID;
  30. // TSIH: With the exception of the Login Final-Response in a new session, this field should
  31. // be set to the TSIH provided by the initiator in the Login Request.
  32. response.TSIH = request.TSIH;
  33. response.InitiatorTaskTag = request.InitiatorTaskTag;
  34. string connectionIdentifier = ConnectionState.GetConnectionIdentifier(session, connection);
  35. if (request.Transit && request.Continue)
  36. {
  37. Log(Severity.Warning, "[{0}] Initiator error: Received login request with both Transit and Continue set to true", connectionIdentifier);
  38. response.Status = LoginResponseStatusName.InitiatorError;
  39. return response;
  40. }
  41. else if (request.Continue)
  42. {
  43. response.Status = LoginResponseStatusName.Success;
  44. return response;
  45. }
  46. if (request.TSIH == 0)
  47. {
  48. // For a new session, the request TSIH is zero,
  49. // As part of the response, the target generates a TSIH.
  50. session.TSIH = m_sessionManager.GetNextTSIH();
  51. session.ISID = request.ISID;
  52. }
  53. response.TSIH = session.TSIH;
  54. response.Status = LoginResponseStatusName.Success;
  55. // RFC 3720: The login process proceeds in two stages - the security negotiation
  56. // stage and the operational parameter negotiation stage. Both stages are optional
  57. // but at least one of them has to be present.
  58. bool firstLoginRequest = (!session.IsDiscovery && session.Target == null);
  59. if (firstLoginRequest)
  60. {
  61. connection.InitiatorName = request.LoginParameters.ValueOf("InitiatorName");
  62. if (String.IsNullOrEmpty(connection.InitiatorName))
  63. {
  64. // RFC 3720: InitiatorName: The initiator of the TCP connection MUST provide this key [..]
  65. // at the first Login of the Login Phase for every connection.
  66. Log(Severity.Warning, "[{0}] Initiator error: InitiatorName was not included in the login request", connectionIdentifier);
  67. response.Status = LoginResponseStatusName.InitiatorError;
  68. return response;
  69. }
  70. string sessionType = request.LoginParameters.ValueOf("SessionType");
  71. if (sessionType == "Discovery")
  72. {
  73. session.IsDiscovery = true;
  74. }
  75. else //sessionType == "Normal" or unspecified (default is Normal)
  76. {
  77. session.IsDiscovery = false;
  78. if (request.LoginParameters.ContainsKey("TargetName"))
  79. {
  80. string targetName = request.LoginParameters.ValueOf("TargetName");
  81. ISCSITarget target = m_targets.FindTarget(targetName);
  82. if (target != null)
  83. {
  84. if (!target.AuthorizeInitiator(connection.InitiatorName, connection.InitiatorEndPoint))
  85. {
  86. Log(Severity.Warning, "[{0}] Initiator was not authorized to access {1}", connectionIdentifier, targetName);
  87. response.Status = LoginResponseStatusName.AuthorizationFailure;
  88. return response;
  89. }
  90. session.Target = target;
  91. }
  92. else
  93. {
  94. Log(Severity.Warning, "[{0}] Initiator requested an unknown target: {1}", connectionIdentifier, targetName);
  95. response.Status = LoginResponseStatusName.NotFound;
  96. return response;
  97. }
  98. }
  99. else
  100. {
  101. // RFC 3720: For any connection within a session whose type is not "Discovery", the first Login Request MUST also include the TargetName key=value pair.
  102. Log(Severity.Warning, "[{0}] Initiator error: TargetName was not included in a non-discovery session", connectionIdentifier);
  103. response.Status = LoginResponseStatusName.InitiatorError;
  104. return response;
  105. }
  106. }
  107. }
  108. if (request.CurrentStage == 0)
  109. {
  110. response.LoginParameters.Add("AuthMethod", "None");
  111. if (session.Target != null)
  112. {
  113. // RFC 3720: During the Login Phase the iSCSI target MUST return the TargetPortalGroupTag key with the first Login Response PDU with which it is allowed to do so
  114. response.LoginParameters.Add("TargetPortalGroupTag", "1");
  115. }
  116. if (request.Transit)
  117. {
  118. if (request.NextStage == 3)
  119. {
  120. session.IsFullFeaturePhase = true;
  121. }
  122. else if (request.NextStage != 1)
  123. {
  124. Log(Severity.Warning, "[{0}] Initiator error: Received login request with Invalid NextStage", connectionIdentifier);
  125. response.Status = LoginResponseStatusName.InitiatorError;
  126. }
  127. }
  128. }
  129. else if (request.CurrentStage == 1)
  130. {
  131. UpdateOperationalParameters(request.LoginParameters, session, connection);
  132. response.LoginParameters = GetLoginOperationalParameters(session, connection);
  133. if (request.Transit)
  134. {
  135. if (request.NextStage == 3)
  136. {
  137. session.IsFullFeaturePhase = true;
  138. }
  139. else
  140. {
  141. Log(Severity.Warning, "[{0}] Initiator error: Received login request with Invalid NextStage", connectionIdentifier);
  142. response.Status = LoginResponseStatusName.InitiatorError;
  143. }
  144. }
  145. }
  146. else
  147. {
  148. // Not valid
  149. Log(Severity.Warning, "[{0}] Initiator error: Received login request with Invalid CurrentStage", connectionIdentifier);
  150. response.Status = LoginResponseStatusName.InitiatorError;
  151. }
  152. return response;
  153. }
  154. private static void UpdateOperationalParameters(KeyValuePairList<string, string> loginParameters, ISCSISession session, ConnectionParameters connectionParameters)
  155. {
  156. string value = loginParameters.ValueOf("MaxRecvDataSegmentLength");
  157. if (value != null)
  158. {
  159. connectionParameters.InitiatorMaxRecvDataSegmentLength = Convert.ToInt32(value);
  160. }
  161. value = loginParameters.ValueOf("MaxConnections");
  162. if (value != null)
  163. {
  164. session.MaxConnections = Math.Min(Convert.ToInt32(value), ISCSIServer.DesiredParameters.MaxConnections);
  165. }
  166. value = loginParameters.ValueOf("InitialR2T");
  167. if (value != null)
  168. {
  169. session.InitialR2T = (value == "Yes") || ISCSIServer.DesiredParameters.InitialR2T;
  170. }
  171. value = loginParameters.ValueOf("ImmediateData");
  172. if (value != null)
  173. {
  174. session.ImmediateData = (value == "Yes") && ISCSIServer.DesiredParameters.ImmediateData;
  175. }
  176. value = loginParameters.ValueOf("MaxBurstLength");
  177. if (value != null)
  178. {
  179. session.MaxBurstLength = Math.Min(Convert.ToInt32(value), ISCSIServer.DesiredParameters.MaxBurstLength);
  180. }
  181. value = loginParameters.ValueOf("FirstBurstLength");
  182. if (value != null)
  183. {
  184. session.FirstBurstLength = Math.Min(Convert.ToInt32(value), ISCSIServer.DesiredParameters.FirstBurstLength);
  185. }
  186. value = loginParameters.ValueOf("DataPDUInOrder");
  187. if (value != null)
  188. {
  189. session.DataPDUInOrder = (value == "Yes") || ISCSIServer.DesiredParameters.DataPDUInOrder;
  190. }
  191. value = loginParameters.ValueOf("DataSequenceInOrder");
  192. if (value != null)
  193. {
  194. session.DataSequenceInOrder = (value == "Yes") || ISCSIServer.DesiredParameters.DataSequenceInOrder;
  195. }
  196. value = loginParameters.ValueOf("DefaultTime2Wait");
  197. if (value != null)
  198. {
  199. session.DefaultTime2Wait = Math.Max(Convert.ToInt32(value), ISCSIServer.DesiredParameters.DefaultTime2Wait);
  200. }
  201. value = loginParameters.ValueOf("DefaultTime2Retain");
  202. if (value != null)
  203. {
  204. session.DefaultTime2Retain = Math.Min(Convert.ToInt32(value), ISCSIServer.DesiredParameters.DefaultTime2Retain);
  205. }
  206. value = loginParameters.ValueOf("MaxOutstandingR2T");
  207. if (value != null)
  208. {
  209. session.MaxOutstandingR2T = Math.Min(Convert.ToInt32(value), ISCSIServer.DesiredParameters.MaxOutstandingR2T);
  210. }
  211. }
  212. private static KeyValuePairList<string, string> GetLoginOperationalParameters(ISCSISession session, ConnectionParameters connectionParameters)
  213. {
  214. KeyValuePairList<string, string> loginParameters = new KeyValuePairList<string, string>();
  215. loginParameters.Add("HeaderDigest", "None");
  216. loginParameters.Add("DataDigest", "None");
  217. loginParameters.Add("MaxRecvDataSegmentLength", connectionParameters.TargetMaxRecvDataSegmentLength.ToString());
  218. if (!session.IsDiscovery)
  219. {
  220. loginParameters.Add("MaxConnections", session.MaxConnections.ToString());
  221. loginParameters.Add("InitialR2T", session.InitialR2T ? "Yes" : "No"); // Microsoft iSCSI Target support InitialR2T = No
  222. loginParameters.Add("ImmediateData", session.ImmediateData ? "Yes" : "No");
  223. loginParameters.Add("MaxBurstLength", session.MaxBurstLength.ToString());
  224. loginParameters.Add("FirstBurstLength", session.FirstBurstLength.ToString());
  225. loginParameters.Add("MaxOutstandingR2T", session.MaxOutstandingR2T.ToString());
  226. loginParameters.Add("DataPDUInOrder", session.DataPDUInOrder ? "Yes" : "No");
  227. loginParameters.Add("DataSequenceInOrder", session.DataSequenceInOrder ? "Yes" : "No");
  228. loginParameters.Add("ErrorRecoveryLevel", session.ErrorRecoveryLevel.ToString());
  229. }
  230. loginParameters.Add("DefaultTime2Wait", session.DefaultTime2Wait.ToString());
  231. loginParameters.Add("DefaultTime2Retain", session.DefaultTime2Retain.ToString());
  232. return loginParameters;
  233. }
  234. }
  235. }