1

我正在尝试为 Netduino 板编写一个 NRPE 解释器。这是一个运行 .NET Micro Framework 4.3 的 Arduino 型板。我在计算协议要求的 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 不是。我调用的例程是来自 Micro 框架的 Utility.ComputeCRC

其他人在这方面也遇到过类似的问题,所以我很幸运能够获得一些线索,可能是什么问题:

NRPE 协议解释

Stack Overflow 上关于 Python 中 CRC'ing NRPE 帖子的帖子

Micro 的 CRC 实现

例如,似乎很清楚原始消息是 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;
            }


        }
    }
4

1 回答 1

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

于 2014-04-02T04:57:40.153 回答