Проблемы с вычислением CRC для протокола NRPE в .NET Micro Framework (Netduino)

Я пытаюсь написать интерпретатор NRPE для платы Netduino. Это плата типа Arduino, работающая под управлением .NET Micro Framework 4.3. У меня возникли проблемы с вычислением CRC, который требует протокол, который выглядит так (фрагмент исходного файла заголовка C++):

typedef struct packet_struct {
int16_t packet_version;
int16_t packet_type;
uint32_t crc32_value;
int16_t result_code;
char buffer[1024];
} packet;

Определенно есть проблемы с порядком байтов, потому что я перехожу от прямого порядка байтов (сеть) к прямому порядку байтов (Netduino/.Net). Я пытался быть осторожным, чтобы реверсировать и реверсировать Int16 и Uint32, когда они входят и выходят из моей структуры. Когда я повторно вывожу пакет, который я прочитал из сети, он идентичен, поэтому я считаю, что многое обрабатывается правильно. Но CRC, который я вычисляю для него, не является. Я вызываю процедуру Utility.ComputeCRC из платформы Micro

У других были аналогичные проблемы в этой области, поэтому мне повезло, что я имею некоторые подсказки, в чем может быть проблема:

Объяснение протокола NRPE

Запись Stack Overflow о CRC-записях NRPE в Python

реализации CRC для Micro

Например, кажется очевидным, что исходное сообщение составляет 1034 байта, дополненное до 1036. Мне не так повезло, так это то, что я нахожусь в ограниченной среде Micro, и весь пример кода для CRC, который я могу найти, обычно включает шаблоны, Linq или другие библиотеки, к которым у меня нет доступа.

Вся помощь приветствуется. Вот пример кода, в котором я безуспешно пытаюсь повторно вычислить CRC из существующего допустимого пакета.

Вывод кода:

Original 1036 bytes: 0002000174D13FD5426E5F4E5250455F434845434B0000000000000000...
Original CRC: 3FD574D1
1036 bytes with zeroed out checksum:    0002000100000000426E5F4E5250455F434845434B00000000000000....
Re-computed checksum (0xFFFF seed): F5B1C55A

Фактический код:

    using System;
    using System.Text;
    // .NET Micro Framework 4.3
    using Microsoft.SPOT;
    using Microsoft.SPOT.Hardware;

    namespace TestApp
    {
        public class Program
        {
            /// <summary>
            ///     These are the bytes as-received from the wire, hex encoded for readability here.
            /// </summary>
            private const string OriginalNetworkBytes = "0002000174D13FD5426E5F4E5250455F434845434B00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
            /// <summary>
            /// Index the CRC starts at in the original message
            /// </summary>
            private const int CrcIndex = 4;

            public static void Main()
            {
                byte[] bufferBytes = StringToByteArrayFastest(OriginalNetworkBytes);
                PrintBytesInHex("Original " + bufferBytes.Length + " bytes: ", bufferBytes);
                UInt32 originalCrc = ParseSwappedUInt32(bufferBytes, CrcIndex);
                Debug.Print("Original CRC: " + originalCrc.ToString("X"));

                // Zero out CRC, then attempt to recompute the CRC
                ZeroOutChecksum(bufferBytes);
                PrintBytesInHex(bufferBytes.Length + " bytes with zeroed out checksum: ", bufferBytes);
                uint computedCrc = Utility.ComputeCRC(bufferBytes, 0, bufferBytes.Length, 0xFFFF);
                Debug.Print("Re-computed checksum (0xFFFF seed): " + computedCrc.ToString("X"));
            }

            /// <summary>
            ///     From this fine Stack Overflow post:
            ///     https://stackoverflow.com/questions/321370/convert-hex-string-to-byte-array
            ///     Because as the author points out, "also works on .NET Micro Framework where (in SDK4.3) byte.Parse(string) only
            ///     permits integer formats."
            /// </summary>
            /// <param name="hex"></param>
            /// <returns></returns>
            public static byte[] StringToByteArrayFastest(string hex)
            {
                if (hex.Length%2 == 1)
                    throw new Exception("The binary key cannot have an odd number of digits");

                var arr = new byte[hex.Length >> 1];

                for (int i = 0; i < hex.Length >> 1; ++i)
                {
                    arr[i] = (byte) ((GetHexVal(hex[i << 1]) << 4) + (GetHexVal(hex[(i << 1) + 1])));
                }

                return arr;
            }

            public static int GetHexVal(char hex)
            {
                int val = hex;
                //For uppercase A-F letters:
                return val - (val < 58 ? 48 : 55);
                //For lowercase a-f letters:
                //return val - (val < 58 ? 48 : 87);
                //Or the two combined, but a bit slower:
                //return val - (val < 58 ? 48 : (val < 97 ? 55 : 87));
            }


            public static UInt32 ParseSwappedUInt32(byte[] byteArray, int arrayIndex)
            {
                byte[] swappedBytes = ByteSwapper(byteArray, arrayIndex, 4);
                return BitConverter.ToUInt32(swappedBytes, 0);
            }

            public static byte[] ByteSwapper(byte[] array, int incomingArrayIndex, int countOfBytesToSwap)
            {
                if (countOfBytesToSwap%2 != 0)
                {
                    throw new Exception("Bytes to be swapped must be divisible by 2; you requested " + countOfBytesToSwap);
                }

                int outgoingArrayIndex = 0;
                byte lastByte = 0;
                var arrayToReturn = new byte[countOfBytesToSwap];
                int finalArrayIndex = incomingArrayIndex + countOfBytesToSwap;
                for (int arrayIndex = incomingArrayIndex; arrayIndex < finalArrayIndex; arrayIndex++)
                {
                    bool isEvenIndex = arrayIndex%2 == 0 || arrayIndex == 0;
                    byte currentByte = array[arrayIndex];
                    if (isEvenIndex)
                    {
                        // Store current byte for next pass through
                        lastByte = currentByte;
                    }
                    else
                    {
                        // Swap two bytes, put into outgoing array
                        arrayToReturn[outgoingArrayIndex] = currentByte;
                        arrayToReturn[outgoingArrayIndex + 1] = lastByte;
                        outgoingArrayIndex += 2;
                    }
                }

                return arrayToReturn;
            }

            private static void ZeroOutChecksum(byte[] messageBytesToClear)
            {
                messageBytesToClear[CrcIndex] = 0;
                messageBytesToClear[CrcIndex + 1] = 0;
                messageBytesToClear[CrcIndex + 2] = 0;
                messageBytesToClear[CrcIndex + 3] = 0;
            }

            /// <summary>
            ///     Debug function to output the message as a hex string
            /// </summary>
            public static void PrintBytesInHex(string messageLabel, byte[] messageBytes)
            {
                string hexString = BytesToHexString(messageBytes);
                Debug.Print(messageLabel + hexString);
            }

            private static string BytesToHexString(byte[] messageBytes)
            {
                var sb = new StringBuilder();
                foreach (byte b in messageBytes)
                {
                    sb.Append(b.ToString("X2"));
                }
                string hexString = sb.ToString();
                return hexString;
            }


        }
    }

person StewLG    schedule 08.03.2014    source источник


Ответы (1)


В конце концов я разработал решение.

Полная документация здесь:

http://www.skyscratch.com/2014/04/02/rats-ate-the-washing-machine-or-a-nagios-nrpe-environmental-monitor-for-netduino/

Соответствующий код CRC выглядит следующим образом:

using System;

namespace FloodSensor
{
/// <summary>
/// Ported from https://github.com/KristianLyng/nrpe/blob/master/src/utils.c
/// I am not sure if this was strictly necessary, but then I could not seem to get Utility.ComputeCRC
/// (http://msdn.microsoft.com/query/dev11.query?appId=Dev11IDEF1&l=EN-US&k=k%28Microsoft.SPOT.Hardware.Utility.ComputeCRC%29;k%28TargetFrameworkMoniker-.NETMicroFramework)
/// to return the same result as this function, no matter what seed I tried with it.
/// </summary>
class NrpeCrc
{
    private const int CrcTableLength = 256;
    static private readonly UInt32[] Crc32Table = new UInt32[CrcTableLength];

    public NrpeCrc()
    {
        generateCrc32Table();
    }

    // Build the crc table - must be called before calculating the crc value
    private void generateCrc32Table()
    {
        const uint poly = 0xEDB88320;
        for (int i = 0; i < 256; i++)
        {
            var crc = (UInt32)i;
            for (int j = 8; j > 0; j--)
            {
                if ((crc & (UInt32)1) > 0)
                {
                    crc = (crc >> 1) ^ poly;
                }
                else
                {
                    crc >>= 1;
                }
            }
            Crc32Table[i] = crc;
        }
    }

    /// <summary>
    /// Calculates the CRC 32 value for a buffer
    /// </summary>
    public UInt32 CalculateCrc32(byte[] buffer, int bufferSize)
    {
        int currentIndex;
        uint crc = 0xFFFFFFFF;

        for (currentIndex = 0; currentIndex < bufferSize; currentIndex++)
        {
            int thisChar = buffer[currentIndex];
            crc = ((crc >> 8) & 0x00FFFFFF) ^ Crc32Table[(crc ^ thisChar) & 0xFF];
        }

        return (crc ^ 0xFFFFFFFF);
    }
}
}

См. также https://github.com/StewLG/NetduinoNrpe/blob/master/FloodSensor/NrpeServer/NrpeCrc.cs

person StewLG    schedule 02.04.2014