ISCSIServer.Login.cs 14 KB

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