C# ICMPv6 校验和计算

Posted

技术标签:

【中文标题】C# ICMPv6 校验和计算【英文标题】:C# ICMPv6 checksum calculation 【发布时间】:2012-04-06 10:11:51 【问题描述】:

我需要计算 ICMPv6 校验和。我在 RFC 2460 中找到了正确的手册,并用 c# 编写了代码,粘贴在下面。在代码中,您可以看到我添加到校验和源 IP、数据包的目标 IP,而不是 ICMPv6 消息长度(邻居广告为 32 个字节),然后我添加下一个标头 ID,即 ICMPv6 的 58。然后是 FOR 循环,它将整个 ICMPv6 消息添加到校验和中(我相信它以消息类型开头,例如 88 00 ... 用于邻居广告)。比我计算和补充校验和,但这是错误的。我正在尝试为真实嗅探的邻居广告计算此校验和,该校验和也在下方,但我无法获得相同的校验和。我的代码可能有什么问题?

static void Main(string[] args)
    

        ICaptureDevice device = new OfflineCaptureDevice("icmp.pcap");
        device.Open();

        device.OnPacketArrival += new SharpPcap.PacketArrivalEventHandler(device_OnPacketArrival);
        device.Capture();


        Console.ReadKey();
    

    private static void device_OnPacketArrival(object sender, SharpPcap.CaptureEventArgs e)
    
        try
        
            //packet conversions
            var packet = PacketDotNet.Packet.ParsePacket(e.Packet);
            var ethernetPacket = ((PacketDotNet.EthernetPacket)packet);
            int dlzka_packetu = e.Packet.Data.Length;
            string eth = BitConverter.ToString(ethernetPacket.Bytes);

            //now its in string field format - one string item is one byte, e.g. FF
            string[] eth_final2 = eth.Split('-');
            foreach (string bytes in eth_final2)  Console.Write(bytes + " "); 
            Console.WriteLine();

            //taking out source IP
            IPAddress src_ip = IPAddress.Parse(eth_final2[22]+eth_final2[23]+":"+eth_final2[24]+eth_final2[25]+":"+eth_final2[26]+eth_final2[27]+":"+eth_final2[28]+eth_final2[29]+":"+eth_final2[30]+eth_final2[31]+":"+eth_final2[32]+eth_final2[33]+":"+eth_final2[34]+eth_final2[35]+":"+eth_final2[36]+eth_final2[37]);

            //destination IP
            IPAddress dst_ip = IPAddress.Parse(eth_final2[38] + eth_final2[39] + ":" + eth_final2[40] + eth_final2[41] + ":" + eth_final2[42] + eth_final2[43] + ":" + eth_final2[44] + eth_final2[45] + ":" + eth_final2[46] + eth_final2[47] + ":" + eth_final2[48] + eth_final2[49] + ":" + eth_final2[50] + eth_final2[51] + ":" + eth_final2[52] + eth_final2[53]);

            Console.WriteLine(src_ip);
            Console.WriteLine(dst_ip);
            int icmpv6_length = 32;
            int next_header = 58;
            Console.WriteLine();

            string icmp_payload = "";

            //taking out ICMPv6 message
            for (int i = 54; i < 54 + 32; i++)
            
                if (i == 56 || i == 57)  icmp_payload += "00"; 
                else icmp_payload += eth_final2[i];
            

            Console.WriteLine(icmp_payload);
            byte[] icmp_bytes = GetStringToBytes(icmp_payload);


            //CALLING THE FUNCTION ICMPchecksum
            ushort sum = ICMPchecksum(src_ip.GetAddressBytes(), dst_ip.GetAddressBytes(), BitConverter.GetBytes(icmpv6_length), BitConverter.GetBytes(next_header), icmp_bytes);

            Console.WriteLine(sum.ToString("X"));


        
        catch (Exception ex)
        
            Console.WriteLine("ERROR");
        


    

    static byte[] GetStringToBytes(string value)
    
        SoapHexBinary shb = SoapHexBinary.Parse(value);
        return shb.Value;
    

    static ushort ICMPchecksum(byte[] src_ip, byte[] dst_ip, byte[] length, byte[] next, byte[] payload)
    
        ushort checksum = 0;

        //length of byte fields
        Console.WriteLine("src_ip: "+src_ip.Length+" dst_ip: "+dst_ip.Length+" length: "+length.Length+" next_header: "+next.Length+" payload: "+payload.Length);

        //display all fields, which will be used for checksum calculation
        Console.WriteLine(BitConverter.ToString(src_ip));
        Console.WriteLine(BitConverter.ToString(dst_ip));
        Console.WriteLine(BitConverter.ToString(length));
        Console.WriteLine(BitConverter.ToString(next));
        Console.WriteLine(BitConverter.ToString(payload));


        //ADDS SOURCE IPv6 address
        checksum += BitConverter.ToUInt16(src_ip, 0);
        checksum += BitConverter.ToUInt16(src_ip, 2);
        checksum += BitConverter.ToUInt16(src_ip, 4);
        checksum += BitConverter.ToUInt16(src_ip, 6);
        checksum += BitConverter.ToUInt16(src_ip, 8);
        checksum += BitConverter.ToUInt16(src_ip, 10);
        checksum += BitConverter.ToUInt16(src_ip, 12);
        checksum += BitConverter.ToUInt16(src_ip, 14);

        //ADDS DEST IPv6 address
        checksum += BitConverter.ToUInt16(dst_ip, 0);
        checksum += BitConverter.ToUInt16(dst_ip, 2);
        checksum += BitConverter.ToUInt16(dst_ip, 4);
        checksum += BitConverter.ToUInt16(dst_ip, 6);
        checksum += BitConverter.ToUInt16(dst_ip, 8);
        checksum += BitConverter.ToUInt16(dst_ip, 10);
        checksum += BitConverter.ToUInt16(dst_ip, 12);
        checksum += BitConverter.ToUInt16(dst_ip, 14);

        //ADDS LENGTH OF ICMPv6 packet
        checksum += BitConverter.ToUInt16(length, 0);
        checksum += BitConverter.ToUInt16(length, 2);

        //ADDS NEXT HEADER ID = 58
        checksum += BitConverter.ToUInt16(next, 0);
        checksum += BitConverter.ToUInt16(next, 2);

        //ADDS WHOLE ICMPv6 message
        for (int i = 0; i < payload.Length; i = i + 2)
        
            Console.WriteLine(i);
            checksum += BitConverter.ToUInt16(payload, i);
        

        checksum += (ushort)(checksum >> 16);

        return (ushort)~checksum;
    

这是数据包屏幕。

唯一链接:http://img831.imageshack.us/img831/7237/icmpv6.png

加上链接,您可以在其中下载文件,我正在使用该链接进行测试:

http://www.2shared.com/file/wIETWTWB/icmp.html

感谢您的帮助。

【问题讨论】:

你能添加函数调用(即你的例子中参数的确切值)吗? “以我相信的消息类型开头” - 您需要确认您的信念是否正确。 该函数的参数是: src_ip = fe80::219:55ff:fe27:27d0 dst_ip = fe80::222:75ff:fed6:fe50 作为有效负载使用 ICMPv6 消息,因为它是我已在我的问题中发布的签名图片。长度为ICMPv6消息的长度为32,next header ID为58。全部转换为byte[]推送到你可以看到的函数 【参考方案1】:

有效载荷用于 ICMPv6 消息,因为它已在我在我的问题中发布的图片中签名

至少那是错误的。计算校验和时,应先将表示校验和本身的字节替换为 0。

更新

根据您的 icmp.pcap,我创建了一个独立程序来计算校验和。完整源代码见下文。

最重要的错误是:

    ushort checksum = 0;

虽然校验和是 16 位的,但该算法需要至少 32 位的临时值:

    uint checksum = 0;

校验和计算需要ones' complement arithmetic,其中加法的进位环绕。为了提高性能,所有的包装都在最后完成;

checksum += (ushort)(checksum >> 16);

但这只有在校验和超过 16 位时才有可能。否则这条线就没用了。

但还有其他需要考虑的因素。 BitConverter.ToUInt16 取决于您的处理器的endianness,它可能是 Little Endian。这将使结果A5AB。这可能不是您所期望的,但是如果您切换字节,您会得到正确的版本。我从here 插入了一个函数来解决这个问题。这将使结果ABA5。另一方面,如果在计算之前没有将校验和设置为 0,那么如果校验和有效,ICMPchecksum 将始终为return 0。另请注意,您的函数无法处理奇数长度的有效负载。

class Program 
    static void Main (string [] args)
    
        byte[] src_ip = new byte[]
                            
                                0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
                                0x02, 0x19, 0x55, 0xff, 0xfe, 0x27, 0x27, 0xd0
                            ;

        byte[] dst_ip = new byte[]
                            
                                0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
                                0x02, 0x22, 0x75, 0xff, 0xfe, 0xd6, 0xfe, 0x50
                            ;
        byte[] length = new byte[] 0, 0, 0, 32;
        byte [] next = new byte []  0, 0, 0, 58 ;
        byte[] payload = new byte[]
                             
                                 0x88, 0x00, 0xab, 0xa5, 0xe0, 0x00, 0x00, 0x00, 
                                 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
                                 0x02, 0x19, 0x55, 0xff, 0xfe, 0x27, 0x27, 0xd0,
                                 0x02, 0x01, 0x00, 0x19, 0x55, 0x27, 0x27, 0xd0
                             ;
#if true
        payload [2] = 0;
        payload [3] = 0;
#endif

        ushort checksum = ICMPchecksum(src_ip, dst_ip, length, next, payload);

        Console.WriteLine (checksum.ToString ("X"));
        Console.ReadKey ();
    

    public static ushort BitConverterToUInt16 (byte [] value, int startIndex)
    
#if false
            return System.BitConverter.ToUInt16 (value, startIndex);
#else
            return System.BitConverter.ToUInt16 (value.Reverse ().ToArray (), value.Length - sizeof (UInt16) - startIndex);
#endif
    

    static ushort ICMPchecksum (byte [] src_ip, byte [] dst_ip, byte [] length, byte [] next, byte [] payload)
    
        uint checksum = 0;

        //length of byte fields
        Console.WriteLine ("src_ip: " + src_ip.Length + " dst_ip: " + dst_ip.Length + " length: " + length.Length + " next_header: " + next.Length + " payload: " + payload.Length);

        //display all fields, which will be used for checksum calculation
        Console.WriteLine (BitConverter.ToString (src_ip));
        Console.WriteLine (BitConverter.ToString (dst_ip));
        Console.WriteLine (BitConverter.ToString (length));
        Console.WriteLine (BitConverter.ToString (next));
        Console.WriteLine (BitConverter.ToString (payload));


        //ADDS SOURCE IPv6 address
        checksum += BitConverterToUInt16 (src_ip, 0);
        checksum += BitConverterToUInt16 (src_ip, 2);
        checksum += BitConverterToUInt16 (src_ip, 4);
        checksum += BitConverterToUInt16 (src_ip, 6);
        checksum += BitConverterToUInt16 (src_ip, 8);
        checksum += BitConverterToUInt16 (src_ip, 10);
        checksum += BitConverterToUInt16 (src_ip, 12);
        checksum += BitConverterToUInt16 (src_ip, 14);

        //ADDS DEST IPv6 address
        checksum += BitConverterToUInt16 (dst_ip, 0);
        checksum += BitConverterToUInt16 (dst_ip, 2);
        checksum += BitConverterToUInt16 (dst_ip, 4);
        checksum += BitConverterToUInt16 (dst_ip, 6);
        checksum += BitConverterToUInt16 (dst_ip, 8);
        checksum += BitConverterToUInt16 (dst_ip, 10);
        checksum += BitConverterToUInt16 (dst_ip, 12);
        checksum += BitConverterToUInt16 (dst_ip, 14);

        //ADDS LENGTH OF ICMPv6 packet
        checksum += BitConverterToUInt16 (length, 0);
        checksum += BitConverterToUInt16 (length, 2);

        //ADDS NEXT HEADER ID = 58
        checksum += BitConverterToUInt16 (next, 0);
        checksum += BitConverterToUInt16 (next, 2);

        //ADDS WHOLE ICMPv6 message
        for (int i = 0; i < payload.Length; i = i + 2) 
            Console.WriteLine (i);
            checksum += BitConverterToUInt16 (payload, i);
        

        checksum += (ushort)(checksum >> 16);

        return (ushort)~checksum;
    

【讨论】:

没错,但我已经在我的代码中,在函数调用之前做到了这一点。我很抱歉我忘了写这个。 ICMPv6 消息被视为图片上的标记,但校验和字段设置为 0000 而不是 aba5。将参数添加到校验和的顺序可能有问题吗?我正在添加源 ip、目标 ip、icmpv6 消息长度、下一个标头 id,然后是 icmpv6 消息......也许这就是问题(或者不是) @user1317200 这就是我要求(完整)函数调用的原因。我也对您使用的算法有疑问。但是我不能仅仅通过阅读代码来判断。但我仍然认为函数调用更有可能存在其他问题,例如创建数组的方式。因为我的答案并不完全正确,所以您可能不应该选择它作为答案。因此,我认为您应该取消标记,然后编辑问题或提出新问题。请添加您使用的调用,以便人们可以复制/粘贴来尝试代码。例如像单元测试一样发布它。 好的,我已经按照您的要求完成了,将整个代码粘贴到问题中,并粘贴链接以下载带有使用过的数据包的 pcap 文件。非常感谢您的关注,希望我们能更正我的代码。我为奇怪的代码道歉,我不是很好的程序员 我已经更新了我的答案,包含的代码返回了正确的校验和。 你不知道,我有多感谢你。代码当然是完美的,我将为捕获的数据包重新制作它。再次感谢,我认为这将帮助更多人寻找答案。祝你有美好的一天。

以上是关于C# ICMPv6 校验和计算的主要内容,如果未能解决你的问题,请参考以下文章

谁会crc16-ccitt的计算方法,用winform做的,谢谢啦。。。

如何计算modbus-rtu的crc校验码

C# CRC16校验码 1.0

什么是校验和?

校验和计算方法

使用Python计算IPTCPUDP校验和