The SMBDirect Data Transfer message is sent to transfer upper-layer data, manage credits, or perform other functions. This request optionally contains upper-layer data to transfer as the message’s data payload. The sender can send a SMBDirect Data Transfer Request message with no data payload to grant credits, request credits, or perform other functions.
Exemplo n.º 1
0
        /// <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));
        }
Exemplo n.º 2
0
        /// <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);
        }
Exemplo n.º 3
0
        /// <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);
        }
Exemplo n.º 4
0
        /// <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
        }
Exemplo n.º 6
0
        /// <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);
 }