/// <summary> /// send SMBD data transfer message, for test case to control the transfer /// mannually /// </summary> /// <param name="creditsRequested">The total number of Send Credits requested /// of the receiver, including any Send Credits already granted. /// </param> /// <param name="creditsGranted">The incremental number of Send Credits /// granted by the sender. /// </param> /// <param name="flags">The flags indicating how the operation is to be processed. /// This field MUST be constructed by using any or none of the following values: /// SMB_DIRECT_RESPONSE_REQUESTED. The Flags field MUST be set to zero if no flag /// values are specified. /// </param> /// <param name="reserved">The sender SHOULD set this field to 0 and the receiver /// MUST ignore it on receipt. /// </param> /// <param name="remainingDataLength">The amount of data, in bytes, remaining in a /// sequence of fragmented messages. If this value is 0x00000000, this message is /// the final message in the sequence. /// </param> /// <param name="dataOffset">The offset, in bytes, from the beginning of the SMBDirect /// header to the first byte of the message’s data payload. If no data payload /// is associated with this message, this value MUST be 0. This offset MUST /// be 8-byte aligned from the beginning of the message. /// </param> /// <param name="dataLength">The length, in bytes, of the message’s data payload. /// If no data payload is associated with this message, this value MUST be 0. /// </param> /// <param name="padding">Additional bytes for alignment</param> /// <param name="buffer"></param> /// <returns>A buffer that contains the data payload as defined by /// the DataOffset and DataLength fields.</returns> public NtStatus SendDataTransferMessage( ushort creditsRequested, ushort creditsGranted, SmbdDataTransfer_Flags flags, ushort reserved, uint remainingDataLength, uint dataOffset, uint dataLength, byte[] padding, byte[] buffer) { SmbdDataTransferMessage smbdDataTransfer = new SmbdDataTransferMessage(); smbdDataTransfer.CreditsRequested = creditsRequested; smbdDataTransfer.CreditsGranted = creditsGranted; smbdDataTransfer.Flags = flags; smbdDataTransfer.Reserved = reserved; smbdDataTransfer.RemainingDataLength = remainingDataLength; smbdDataTransfer.DataOffset = dataOffset; smbdDataTransfer.DataLength = dataLength; smbdDataTransfer.Padding = padding; smbdDataTransfer.Buffer = buffer; return(SendDataTransferMessage(smbdDataTransfer)); }
/// <summary> /// send SMBDirect data transfer message /// </summary> /// <param name="dataTransferMessage">SMBDirect data transfer message</param> /// <returns></returns> public NtStatus SendDataTransferMessage(SmbdDataTransferMessage dataTransferMessage) { byte[] data = TypeMarshal.ToBytes <SmbdDataTransferMessage>(dataTransferMessage); // register a buffer NtStatus status = Connection.Endpoint.SendData(data); return(status); }
/// <summary> /// receive SMBDirect data transfer message /// </summary> /// <param name="dataTransferMessage">Received SMBDirect data transfer message</param> /// <returns></returns> public NtStatus ReceiveDataTransferMessage(TimeSpan timeout, out SmbdDataTransferMessage dataTransferMessage) { dataTransferMessage = new SmbdDataTransferMessage(); // get the memory byte[] data; NtStatus status = Connection.Endpoint.ReceiveData(timeout, out data); if (status != NtStatus.STATUS_SUCCESS) { return(status); } // decode dataTransferMessage = TypeMarshal.ToStruct <SmbdDataTransferMessage>(data); // log transfer package LogEvent(string.Format( @"Receive SMBD Data Transfer Credit Request: {0} Credit Granted: {1} Flags: {2} Reserved: {3} RemainingDataLength: {4} DataOffset: {5} Buffer Length: {6}", dataTransferMessage.CreditsRequested, dataTransferMessage.CreditsGranted, dataTransferMessage.Flags, dataTransferMessage.Reserved, dataTransferMessage.RemainingDataLength, dataTransferMessage.DataOffset, dataTransferMessage.Buffer.Length)); // The value of Connection.ReceiveCredits MUST be decremented by one. Connection.ReceiveCredits--; Connection.ReceiveCreditTarget = dataTransferMessage.CreditsRequested; if (dataTransferMessage.CreditsGranted > 0) { Connection.SendCredits += dataTransferMessage.CreditsGranted; } return(NtStatus.STATUS_SUCCESS); }
/// <summary> /// split fragmented message to multiple SMBDirect segments /// </summary> /// <param name="message">Input data</param> /// <param name="segmentLength">length of each segment</param> /// <returns></returns> public static List <SmbdDataTransferMessage> SplitData2Segments( byte[] message, uint segmentLength) { uint offset = 0; // current block offset // calculate the size of data each segment can send List <SmbdDataTransferMessage> ret = new List <SmbdDataTransferMessage>(); while (offset < message.Length) { uint blockLength; if (message.Length - offset > segmentLength) { blockLength = segmentLength; } else { blockLength = (uint)message.Length - offset; } byte[] tmp = new byte[blockLength]; Array.Copy(message, offset, tmp, 0, blockLength); offset += blockLength; // enqueue SmbdDataTransferMessage transferMsg = new SmbdDataTransferMessage(); transferMsg.CreditsGranted = 0; transferMsg.CreditsRequested = 0; transferMsg.Flags = 0; transferMsg.Reserved = 0; transferMsg.RemainingDataLength = (uint)message.Length - offset; transferMsg.DataOffset = (uint)SmbdDataTransferMessage.DEFAULT_DATA_OFFSET; transferMsg.DataLength = blockLength; transferMsg.Buffer = tmp; transferMsg.Padding = new byte[SmbdDataTransferMessage.DEFAULT_DATA_OFFSET - SmbdDataTransferMessage.MINIMUM_SIZE]; ret.Add(transferMsg); } return(ret); }
/// <summary> /// Receive and validate SMB2 Read Response /// </summary> /// <param name="readResponseSize">Size of SMB2 read response</param> public void ValidateReadResponse(int credits, int readResponseSize) { // receive read response, maybe in several SMBDirect data transfer messages SmbdDataTransferMessage transferPackage = new SmbdDataTransferMessage(); byte[] readResponsePacket = new byte[readResponseSize]; int offset = 0; NtStatus status; while (offset < readResponseSize) { while (smbdAdapter.ClientConnection.ReceiveCredits > 0) { status = (NtStatus)smbdAdapter.SmbdReceivDataTransferMessage( smbdAdapter.TestConfig.Smb2ConnectionTimeout, out transferPackage ); BaseTestSite.Log.Add( LogEntryKind.Debug, "Received a SMBDirect Data Transfer message, current Connection.ReceiveCredits is {0}", smbdAdapter.ClientConnection.ReceiveCredits); #region Assert BaseTestSite.Assert.AreEqual<NtStatus>( NtStatus.STATUS_SUCCESS, status, "Received SMBDirect Data Transfer Message with status {0}", status); BaseTestSite.Assert.AreEqual<uint>( (uint)(readResponseSize - offset), transferPackage.DataLength + transferPackage.RemainingDataLength, "Received SMBDirect Data Transfer Message: DataLength {0}, RemainingDataLength {1}", transferPackage.DataLength, transferPackage.RemainingDataLength); BaseTestSite.Assert.AreEqual<uint>( 0, transferPackage.DataOffset % 8, "Received SMBDirect Data Transfer Message: DataOffset {0}, which should be 8-byte aligned", transferPackage.DataOffset); #endregion Array.Copy(transferPackage.Buffer, 0, readResponsePacket, offset, transferPackage.DataLength); offset += (int)transferPackage.DataLength; if (transferPackage.RemainingDataLength == 0) { break; } } BaseTestSite.Assert.IsTrue(transferPackage.CreditsGranted > 0, "SMBDirect Data Tranfer message with last credit should with CreditsGranted greater than 0"); #region send empty message to grant credit status = NtStatus.STATUS_SUCCESS; do { status = smbdAdapter.SmbdPostReceive(); } while (status == NtStatus.STATUS_SUCCESS && smbdAdapter.ClientConnection.ReceiveCredits < credits); // check receive queue before sending BaseTestSite.Assert.AreEqual<int>( 0, smbdAdapter.ReceiveEntryInQueue, "No message should be sent from server"); smbdAdapter.SmbdSendDataTransferMessage( smbdAdapter.ClientConnection.SendCreditTarget, (ushort)smbdAdapter.ClientConnection.ReceiveCredits, SmbdDataTransfer_Flags.NONE, 0, 0, 0, new byte[0]); BaseTestSite.Log.Add( LogEntryKind.Debug, "Grant {0} credits to peer, current Connection.ReceiveCredits is {0}", smbdAdapter.ClientConnection.ReceiveCredits); #endregion } #region Decode SMB2 READ response and validate object endpoint = new object(); int consumedLength; int expectedLength; StackPacket[] stackPackets = smbdAdapter.Decoder.Smb2DecodePacketCallback( endpoint, readResponsePacket, out consumedLength, out expectedLength); Smb2ReadResponsePacket package = (Smb2ReadResponsePacket)stackPackets[0]; READ_Response readResponse = package.PayLoad; BaseTestSite.Assert.AreEqual<NtStatus>( NtStatus.STATUS_SUCCESS, (NtStatus)package.Header.Status, "Status of SMB2 Read File is {0}", (NtStatus)package.Header.Status); #endregion }
/// <summary> /// Upper layer requests that the SMBDirect Protocol sends a message. /// /// send SMBDirect message. In this function, the data will disassemble to multiple segments /// if the data size is greater than Connection.MaxSendSize. /// </summary> /// <param name="message">data to send</param> public NtStatus SendMessage(byte[] message) { uint offset = 0; // current block offset // calculate the size of data each segment can send uint bufferSize = Connection.MaxSendSize - (uint)SmbdDataTransferMessage.DEFAULT_DATA_OFFSET; while (offset < message.Length) { uint blockLength; if (message.Length - offset > bufferSize) { blockLength = bufferSize; } else { blockLength = (uint)message.Length - offset; } byte[] tmp = new byte[blockLength]; Array.Copy(message, offset, tmp, 0, blockLength); offset += blockLength; // enqueue SmbdDataTransferMessage transferMsg = new SmbdDataTransferMessage(); transferMsg.CreditsGranted = 0; transferMsg.CreditsRequested = 0; transferMsg.Flags = 0; transferMsg.Reserved = 0; transferMsg.RemainingDataLength = (uint)message.Length - offset; transferMsg.DataOffset = (uint)SmbdDataTransferMessage.DEFAULT_DATA_OFFSET; transferMsg.DataLength = blockLength; transferMsg.Buffer = tmp; transferMsg.Padding = new byte[SmbdDataTransferMessage.DEFAULT_DATA_OFFSET - SmbdDataTransferMessage.MINIMUM_SIZE]; Connection.SendQueue.Enqueue(transferMsg); } #region dequeue // grant the first message if exist bool isFirst = true; while (Connection.SendQueue.Count > 0) { isFirst = true; while (Connection.SendQueue.Count > 0) { // If Connection.SendCredits is 0, stop processing messages, and break the loop. if (Connection.SendCredits == 0) { break; } SmbdDataTransferMessage transferMsg = Connection.SendQueue.Peek(); if (isFirst) { isFirst = false; int creditIncrement = ManageCredits(false); if (Connection.SendQueue.Count != 0) { transferMsg.CreditsGranted = (ushort)creditIncrement; } } /// If Connection.SendCredits is 1 and the CreditsGranted field of the message is 0, /// then at least one credit MUST be granted to the peer to prevent deadlock. if (Connection.SendCredits == 1 && transferMsg.CreditsGranted == 0) { int creditIncrement = ManageCredits(false); if (creditIncrement == 0) {// If the processing specified in section 3.1.5.9 returns zero, stop processing Sends break; } // increment the CreditsGranted transferMsg.CreditsGranted += (ushort)creditIncrement; } Connection.SendQueue.Dequeue(); Connection.SendCredits--; transferMsg.CreditsRequested = Connection.SendCreditTarget; /// If Connection.KeepaliveRequested is "PENDING", the Flags field of the message MUST be /// set to SMB_DIRECT_RESPONSE_REQUESTED, Connection.KeepaliveRequested MUST be set to "SENT", /// and the Idle Connection Timer MUST be reset to 5 seconds. Otherwise, the Flags field of the message MUST be set to 0x0000. /// /// No need follow the description in SDK. Test case checks the statement. // send message NtStatus status = SendDataTransferMessage(transferMsg); if (status != NtStatus.STATUS_SUCCESS) { return(status); } } // receive the credit granted message, when all send credits has been comsumed if (Connection.SendQueue.Count > 0 && Connection.SendCredits == 0) { // receive the empty message for granting send credits SmbdDataTransferMessage transferPackage; this.ReceiveDataTransferMessage( TimeSpan.FromSeconds(SmbdConnection.KEEP_ALIVE_INTERVAL), out transferPackage ); } else { break; } } #endregion return(NtStatus.STATUS_SUCCESS); }
/// <summary> /// Send multiple segments /// /// If isMininum is not set, generate list of number from 128 to 0 and then 0 to ~. /// For example, 128, 127, 126 ... 1, 0, 1, 2, ... /// Else, generate list of number with all number one: 1, 1, 1, ..... /// /// Split the input data into slices. Data length of each slice follows the number and number sequence in the list just generated. /// /// Send the SMBD Data Transfer messages to server. /// </summary> /// <param name="data">Data to be sent.</param> /// <param name="isMininum">Indicate whether the function is used by test case "SmbdDataTransfer_SmallLengthSegment".</param> /// <param name="withRedundancyBytes">With Redundancy Bytes at the end of SMBD Data Transfer messages</param> public void SendMultipleSegmentsWithVariableDataLength( byte[] data, bool isMininum = false, bool withRedundancyBytes = false) { int offset = 0; int startSize = 128; bool isAdd = false; NtStatus status; while (offset < data.Length) { #region send SMBDirect Data Transfer while (offset < data.Length && smbdAdapter.ClientConnection.SendCredits > 0) { int currentSize = 0; #region Calculate current segment size if (isMininum) { currentSize = 1; } else { currentSize = startSize; if (isAdd) { startSize++; } else { startSize--; if (startSize == -1) { isAdd = true; startSize = 0; } } } #endregion // if is exceed if (currentSize > smbdAdapter.ClientConnection.MaxReceiveSize) { currentSize = (int)smbdAdapter.ClientConnection.MaxReceiveSize; } if (offset + currentSize > data.Length) { currentSize = data.Length - offset; } byte[] segmentData = new byte[currentSize]; Array.Copy(data, offset, segmentData, 0, currentSize); offset += currentSize; SmbdDataTransferMessage smbdDataTransferMessage = new SmbdDataTransferMessage(); smbdDataTransferMessage.CreditsRequested = 255; smbdDataTransferMessage.CreditsGranted = 1; smbdDataTransferMessage.Flags = SmbdDataTransfer_Flags.NONE; smbdDataTransferMessage.RemainingDataLength = (uint)(data.Length - offset); smbdDataTransferMessage.DataOffset = 24; smbdDataTransferMessage.DataLength = (uint)currentSize; smbdDataTransferMessage.Padding = new byte[4]; smbdDataTransferMessage.Buffer = segmentData; byte[] smbdDataTransferData = TypeMarshal.ToBytes<SmbdDataTransferMessage>(smbdDataTransferMessage); if (withRedundancyBytes) { byte[] dataWithMaxSendSize = new byte[smbdAdapter.ServerConnection.MaxReceiveSize]; Array.Copy(smbdDataTransferData, dataWithMaxSendSize, smbdDataTransferData.Length); smbdDataTransferData = dataWithMaxSendSize; } status = smbdAdapter.SendDataOverRdma(smbdDataTransferData); smbdAdapter.ClientConnection.SendCredits--; BaseTestSite.Assert.AreEqual<NtStatus>( NtStatus.STATUS_SUCCESS, status, "Status of send segment with length {0}, {1} send credit remaining.", currentSize, smbdAdapter.ClientConnection.SendCredits ); } #endregion #region receive credit grant if (offset < data.Length) { // receive the empty message for granting send credits SmbdDataTransferMessage transferPackage; status = (NtStatus)smbdAdapter.SmbdReceivDataTransferMessage( TimeSpan.FromSeconds(SmbdConnection.KEEP_ALIVE_INTERVAL), out transferPackage ); BaseTestSite.Assert.AreEqual<uint>(0, transferPackage.DataLength, "Empty message"); #region Check the empty message, which is to grant credit BaseTestSite.Assert.AreEqual<NtStatus>(NtStatus.STATUS_SUCCESS, status, "Status of receive SMB2 negotiate response {0}", status); BaseTestSite.Assert.IsTrue(transferPackage.CreditsGranted > 0, "CreditsGranted of Response is {0}", transferPackage.CreditsRequested); BaseTestSite.Assert.IsTrue(transferPackage.CreditsRequested > 0, "CreditsRequested of Response is {0}", transferPackage.CreditsRequested); BaseTestSite.Assert.AreEqual<SmbdDataTransfer_Flags>(SmbdDataTransfer_Flags.NONE, transferPackage.Flags, "Flags of Response is {0}", transferPackage.Flags); BaseTestSite.Assert.AreEqual<ushort>(0, transferPackage.Reserved, "Reserved of Response is {0}", transferPackage.Reserved); BaseTestSite.Assert.AreEqual<uint>(0, transferPackage.RemainingDataLength, "Reserved of Response is {0}", transferPackage.RemainingDataLength); BaseTestSite.Assert.AreEqual<uint>(0, transferPackage.DataOffset, "DataOffset of Response is {0}", transferPackage.DataOffset); BaseTestSite.Assert.AreEqual<uint>(0, transferPackage.DataLength, "DataLength of Response is {0}", transferPackage.DataLength); BaseTestSite.Assert.AreEqual<uint>(0, (uint)transferPackage.Padding.Length, "Padding length is {0}", transferPackage.Padding.Length); #endregion // raise receive request for write response status = smbdAdapter.SmbdPostReceive(); } #endregion } }
public void SmbdDataTransfer_UncompletedMessage() { // define data for test case const uint MAX_SEND_SIZE = 1024; const uint MAX_FRAGMENT_SIZE = 128 * 1024; // 128 KiB const uint MAX_RECEIVE_SIZE = 1024; const int UNCOMPLETE_SIZE = 19; BaseTestSite.Log.Add(LogEntryKind.TestStep, "Connect to server over RDMA"); NtStatus status = smbdAdapter.ConnectToServerOverRDMA(); BaseTestSite.Assert.AreEqual<NtStatus>(NtStatus.STATUS_SUCCESS, status, "Status of SMBD connection is {0}", status); BaseTestSite.Log.Add(LogEntryKind.TestStep, "SMBD Negotiate"); SmbdNegotiateResponse response; status = smbdAdapter.SmbdNegotiate(MAX_SEND_SIZE, MAX_RECEIVE_SIZE, MAX_FRAGMENT_SIZE, out response); BaseTestSite.Assert.AreEqual<NtStatus>(NtStatus.STATUS_SUCCESS, status, "Status of SMBD negotiate is {0}", status); BaseTestSite.Log.Add(LogEntryKind.TestStep, "Generate a 19-bytes packet."); SmbdDataTransferMessage dataTransfer = new SmbdDataTransferMessage(); dataTransfer.CreditsRequested = smbdAdapter.ClientConnection.SendCreditTarget; dataTransfer.CreditsGranted = 1; dataTransfer.Flags = SmbdDataTransfer_Flags.NONE; dataTransfer.RemainingDataLength = 0; dataTransfer.DataOffset = 0; dataTransfer.DataLength = 0; dataTransfer.Padding = new byte[0]; dataTransfer.Buffer = new byte[0]; byte[] dataTransferPackage = TypeMarshal.ToBytes<SmbdDataTransferMessage>(dataTransfer); byte[] uncompleteDataTransferPackage = new byte[UNCOMPLETE_SIZE]; Array.Copy(dataTransferPackage, uncompleteDataTransferPackage, UNCOMPLETE_SIZE); // send message out BaseTestSite.Log.Add(LogEntryKind.TestStep, "Send the uncompleted Data Transfer package to server."); status = smbdAdapter.SendDataOverRdma(uncompleteDataTransferPackage); BaseTestSite.Assert.AreEqual<NtStatus>(status, NtStatus.STATUS_SUCCESS, "Status of send SMBDirect Data Transfer message is {0}", status); // wait for connection to be terminated smbdAdapter.WaitRdmaDisconnect(); BaseTestSite.Log.Add(LogEntryKind.TestStep, "Verify server connection will be terminated."); BaseTestSite.Assert.IsFalse(smbdAdapter.ClientConnection.Endpoint.IsConnected, "Connection should be terminated."); }
/// <summary> /// Modify DataOffset in SMBDirect Data Transfer message /// </summary> public void CommonTestMethod_ModifyField_SmbdDataTransfer( uint maxSendSize, uint maxFragmentSize, uint maxReceiveSize, uint bufferLength, int modifyFieldAddress, uint modifyValue, bool useMaxSendSize = false ) { byte[] smb2WriteRequest; byte[] fileContent; byte[] orgDataTransferPackage; SmbdDataTransferMessage dataTransfer; NtStatus status; if (useMaxSendSize) { Initialize_SendSmbdDataTransferTestCase( maxSendSize, maxFragmentSize, maxReceiveSize, 24, // data offset out fileContent, out smb2WriteRequest, true); } else { Initialize_SendSmbdDataTransferTestCase( maxSendSize, maxFragmentSize, maxReceiveSize, bufferLength, out fileContent, out smb2WriteRequest); } dataTransfer = new SmbdDataTransferMessage(); dataTransfer.CreditsRequested = 255; dataTransfer.CreditsGranted = 1; dataTransfer.Flags = SmbdDataTransfer_Flags.NONE; dataTransfer.RemainingDataLength = 0; dataTransfer.DataOffset = 24; dataTransfer.DataLength = (uint)smb2WriteRequest.Length; dataTransfer.Padding = new byte[4]; dataTransfer.Buffer = smb2WriteRequest; // Change field value in SMBDirect Data Transfer message BaseTestSite.Log.Add(LogEntryKind.TestStep, "Change field value in SMBDirect Data Transfer message."); orgDataTransferPackage = TypeMarshal.ToBytes<SmbdDataTransferMessage>(dataTransfer); byte[] invalidDataTransferPackage = new byte[orgDataTransferPackage.Length]; if (useMaxSendSize) { modifyValue += (uint)smb2WriteRequest.Length; } byte[] dataOffsetByte = TypeMarshal.ToBytes<uint>(modifyValue); Array.Copy(orgDataTransferPackage, invalidDataTransferPackage, orgDataTransferPackage.Length); Array.Copy(dataOffsetByte, 0, invalidDataTransferPackage, modifyFieldAddress, dataOffsetByte.Length); // send data over RDMA BaseTestSite.Log.Add(LogEntryKind.TestStep, "Send data over RDMA"); status = smbdAdapter.SendDataOverRdma(invalidDataTransferPackage); BaseTestSite.Assert.AreEqual<NtStatus>(status, NtStatus.STATUS_SUCCESS, "Status of send SMBDirect Data Transfer message is {0}", status); BaseTestSite.Log.Add(LogEntryKind.TestStep, "Verify server connection will be terminated."); smbdAdapter.WaitRdmaDisconnect(); BaseTestSite.Assert.IsFalse(smbdAdapter.ClientConnection.Endpoint.IsConnected, "Connection should be terminated."); }
/// <summary> /// split fragmented message to multiple SMBDirect segments /// </summary> /// <param name="message">Input data</param> /// <param name="segmentLength">length of each segment</param> /// <returns></returns> public static List<SmbdDataTransferMessage> SplitData2Segments( byte[] message, uint segmentLength) { uint offset = 0; // current block offset // calculate the size of data each segment can send List<SmbdDataTransferMessage> ret = new List<SmbdDataTransferMessage>(); while (offset < message.Length) { uint blockLength; if (message.Length - offset > segmentLength) { blockLength = segmentLength; } else { blockLength = (uint)message.Length - offset; } byte[] tmp = new byte[blockLength]; Array.Copy(message, offset, tmp, 0, blockLength); offset += blockLength; // enqueue SmbdDataTransferMessage transferMsg = new SmbdDataTransferMessage(); transferMsg.CreditsGranted = 0; transferMsg.CreditsRequested = 0; transferMsg.Flags = 0; transferMsg.Reserved = 0; transferMsg.RemainingDataLength = (uint)message.Length - offset; transferMsg.DataOffset = (uint)SmbdDataTransferMessage.DEFAULT_DATA_OFFSET; transferMsg.DataLength = blockLength; transferMsg.Buffer = tmp; transferMsg.Padding = new byte[SmbdDataTransferMessage.DEFAULT_DATA_OFFSET - SmbdDataTransferMessage.MINIMUM_SIZE]; ret.Add(transferMsg); } return ret; }
public void SmbdDataTransfer_Redundancy() { // define data for test case const uint MAX_SEND_SIZE = 1024; const uint MAX_FRAGMENT_SIZE = 128 * 1024; // 128 KiB const uint MAX_RECEIVE_SIZE = 1024; byte[] smb2WriteRequest; byte[] fileContent; Initialize_SendSmbdDataTransferTestCase( MAX_SEND_SIZE, MAX_FRAGMENT_SIZE, MAX_RECEIVE_SIZE, smbdAdapter.TestConfig.SmallFileSizeInByte, out fileContent, out smb2WriteRequest); SmbdDataTransferMessage dataTransferMessage = new SmbdDataTransferMessage(); dataTransferMessage.CreditsRequested = 255; dataTransferMessage.CreditsGranted = 1; dataTransferMessage.Flags = SmbdDataTransfer_Flags.NONE; dataTransferMessage.DataOffset = 24; dataTransferMessage.RemainingDataLength = 0; dataTransferMessage.Reserved = 0; dataTransferMessage.Padding = new byte[4]; dataTransferMessage.DataLength = (uint)smb2WriteRequest.Length; dataTransferMessage.Buffer = smb2WriteRequest; // generate bytes and add Redundancy bytes byte[] smbdBytes = TypeMarshal.ToBytes<SmbdDataTransferMessage>(dataTransferMessage); byte[] smbdBytesWithRedundancy = new byte[MAX_SEND_SIZE]; Array.Copy(smbdBytes, smbdBytesWithRedundancy, smbdBytes.Length); BaseTestSite.Log.Add(LogEntryKind.TestStep, "Send Data with redundancy bytes over RDMA."); smbdAdapter.SendDataOverRdma(smbdBytesWithRedundancy); BaseTestSite.Log.Add(LogEntryKind.TestStep, "Validate Write response."); ValidateWriteResponse(fileContent); BaseTestSite.Log.Add(LogEntryKind.TestStep, "Validate File content."); ValidateFileContent(fileContent); BaseTestSite.Log.Add(LogEntryKind.TestStep, "Close file and disconnect from server."); smbdAdapter.Smb2CloseFile(); // disconnect smbdAdapter.DisconnectRdma(); }
/// <summary> /// Upper layer requests that the SMBDirect Protocol sends a message. /// /// send SMBDirect message. In this function, the data will disassemble to multiple segments /// if the data size is greater than Connection.MaxSendSize. /// </summary> /// <param name="message">data to send</param> public NtStatus SendMessage(byte[] message) { uint offset = 0; // current block offset // calculate the size of data each segment can send uint bufferSize = Connection.MaxSendSize - (uint)SmbdDataTransferMessage.DEFAULT_DATA_OFFSET; while (offset < message.Length) { uint blockLength; if (message.Length - offset > bufferSize) { blockLength = bufferSize; } else { blockLength = (uint)message.Length - offset; } byte[] tmp = new byte[blockLength]; Array.Copy(message, offset, tmp, 0, blockLength); offset += blockLength; // enqueue SmbdDataTransferMessage transferMsg = new SmbdDataTransferMessage(); transferMsg.CreditsGranted = 0; transferMsg.CreditsRequested = 0; transferMsg.Flags = 0; transferMsg.Reserved = 0; transferMsg.RemainingDataLength = (uint)message.Length - offset; transferMsg.DataOffset = (uint)SmbdDataTransferMessage.DEFAULT_DATA_OFFSET; transferMsg.DataLength = blockLength; transferMsg.Buffer = tmp; transferMsg.Padding = new byte[SmbdDataTransferMessage.DEFAULT_DATA_OFFSET - SmbdDataTransferMessage.MINIMUM_SIZE]; Connection.SendQueue.Enqueue(transferMsg); } #region dequeue // grant the first message if exist bool isFirst = true; while (Connection.SendQueue.Count > 0) { isFirst = true; while (Connection.SendQueue.Count > 0) { // If Connection.SendCredits is 0, stop processing messages, and break the loop. if (Connection.SendCredits == 0) { break; } SmbdDataTransferMessage transferMsg = Connection.SendQueue.Peek(); if (isFirst) { isFirst = false; int creditIncrement = ManageCredits(false); if (Connection.SendQueue.Count != 0) { transferMsg.CreditsGranted = (ushort)creditIncrement; } } /// If Connection.SendCredits is 1 and the CreditsGranted field of the message is 0, /// then at least one credit MUST be granted to the peer to prevent deadlock. if (Connection.SendCredits == 1 && transferMsg.CreditsGranted == 0) { int creditIncrement = ManageCredits(false); if (creditIncrement == 0) {// If the processing specified in section 3.1.5.9 returns zero, stop processing Sends break; } // increment the CreditsGranted transferMsg.CreditsGranted += (ushort)creditIncrement; } Connection.SendQueue.Dequeue(); Connection.SendCredits--; transferMsg.CreditsRequested = Connection.SendCreditTarget; /// If Connection.KeepaliveRequested is "PENDING", the Flags field of the message MUST be /// set to SMB_DIRECT_RESPONSE_REQUESTED, Connection.KeepaliveRequested MUST be set to "SENT", /// and the Idle Connection Timer MUST be reset to 5 seconds. Otherwise, the Flags field of the message MUST be set to 0x0000. /// /// No need follow the description in SDK. Test case checks the statement. // send message NtStatus status = SendDataTransferMessage(transferMsg); if (status != NtStatus.STATUS_SUCCESS) { return status; } } // receive the credit granted message, when all send credits has been comsumed if (Connection.SendQueue.Count > 0 && Connection.SendCredits == 0) { // receive the empty message for granting send credits SmbdDataTransferMessage transferPackage; this.ReceiveDataTransferMessage( TimeSpan.FromSeconds(SmbdConnection.KEEP_ALIVE_INTERVAL), out transferPackage ); } else { break; } } #endregion return NtStatus.STATUS_SUCCESS; }
/// <summary> /// send SMBDirect data transfer message /// </summary> /// <param name="dataTransferMessage">SMBDirect data transfer message</param> /// <returns></returns> public NtStatus SendDataTransferMessage(SmbdDataTransferMessage dataTransferMessage) { byte[] data = TypeMarshal.ToBytes<SmbdDataTransferMessage>(dataTransferMessage); // register a buffer NtStatus status = Connection.Endpoint.SendData(data); return status; }
/// <summary> /// send SMBD data transfer message, for test case to control the transfer /// mannually /// </summary> /// <param name="creditsRequested">The total number of Send Credits requested /// of the receiver, including any Send Credits already granted. /// </param> /// <param name="creditsGranted">The incremental number of Send Credits /// granted by the sender. /// </param> /// <param name="flags">The flags indicating how the operation is to be processed. /// This field MUST be constructed by using any or none of the following values: /// SMB_DIRECT_RESPONSE_REQUESTED. The Flags field MUST be set to zero if no flag /// values are specified. /// </param> /// <param name="reserved">The sender SHOULD set this field to 0 and the receiver /// MUST ignore it on receipt. /// </param> /// <param name="remainingDataLength">The amount of data, in bytes, remaining in a /// sequence of fragmented messages. If this value is 0x00000000, this message is /// the final message in the sequence. /// </param> /// <param name="dataOffset">The offset, in bytes, from the beginning of the SMBDirect /// header to the first byte of the message’s data payload. If no data payload /// is associated with this message, this value MUST be 0. This offset MUST /// be 8-byte aligned from the beginning of the message. /// </param> /// <param name="dataLength">The length, in bytes, of the message’s data payload. /// If no data payload is associated with this message, this value MUST be 0. /// </param> /// <param name="padding">Additional bytes for alignment</param> /// <param name="buffer"></param> /// <returns>A buffer that contains the data payload as defined by /// the DataOffset and DataLength fields.</returns> public NtStatus SendDataTransferMessage( ushort creditsRequested, ushort creditsGranted, SmbdDataTransfer_Flags flags, ushort reserved, uint remainingDataLength, uint dataOffset, uint dataLength, byte[] padding, byte[] buffer) { SmbdDataTransferMessage smbdDataTransfer = new SmbdDataTransferMessage(); smbdDataTransfer.CreditsRequested = creditsRequested; smbdDataTransfer.CreditsGranted = creditsGranted; smbdDataTransfer.Flags = flags; smbdDataTransfer.Reserved = reserved; smbdDataTransfer.RemainingDataLength = remainingDataLength; smbdDataTransfer.DataOffset = dataOffset; smbdDataTransfer.DataLength = dataLength; smbdDataTransfer.Padding = padding; smbdDataTransfer.Buffer = buffer; return SendDataTransferMessage(smbdDataTransfer); }
/// <summary> /// receive SMBDirect data transfer message /// </summary> /// <param name="dataTransferMessage">Received SMBDirect data transfer message</param> /// <returns></returns> public NtStatus ReceiveDataTransferMessage(TimeSpan timeout, out SmbdDataTransferMessage dataTransferMessage) { dataTransferMessage = new SmbdDataTransferMessage(); // get the memory byte[] data; NtStatus status = Connection.Endpoint.ReceiveData(timeout, out data); if (status != NtStatus.STATUS_SUCCESS) { return status; } // decode dataTransferMessage = TypeMarshal.ToStruct<SmbdDataTransferMessage>(data); // log transfer package LogEvent(string.Format( @"Receive SMBD Data Transfer Credit Request: {0} Credit Granted: {1} Flags: {2} Reserved: {3} RemainingDataLength: {4} DataOffset: {5} Buffer Length: {6}", dataTransferMessage.CreditsRequested, dataTransferMessage.CreditsGranted, dataTransferMessage.Flags, dataTransferMessage.Reserved, dataTransferMessage.RemainingDataLength, dataTransferMessage.DataOffset, dataTransferMessage.Buffer.Length)); // The value of Connection.ReceiveCredits MUST be decremented by one. Connection.ReceiveCredits--; Connection.ReceiveCreditTarget = dataTransferMessage.CreditsRequested; if (dataTransferMessage.CreditsGranted > 0) { Connection.SendCredits += dataTransferMessage.CreditsGranted; } return NtStatus.STATUS_SUCCESS; }
public NtStatus SmbdReceivDataTransferMessage( TimeSpan timeout, out SmbdDataTransferMessage transferMsg ) { return client.SmbdReceiveDataTransferMessage(timeout, out transferMsg); }