Thread: Ping and Traceroute not working in IPv6 environment on Windows XP / Win Server 2003

  1. #1
    Registered User
    Join Date
    Apr 2009
    Location
    Bangalore
    Posts
    6

    Ping and Traceroute not working in IPv6 environment on Windows XP / Win Server 2003

    Hi Guys,
    For a reqt, we are doing ping and traceroute on our application using Raw Sockets. This code supports both IPv4 and IPv6. On IPv4 environment, both ping and traceroute works fine in Vista / Non Vista (Win XP, Win 2003). But on IPv6 environment, both ping and tracroute are working fine only on Vista and not working on Windows XP or Windows 2003. We tried with ethereal and captured all the packets that are being send during ping. It says that "checksum incorrect" in the ping request. When gone through ICMPv6 RFC, they mentioned that checksum has to be calculated along with Pseudo-header. We included pseudo-header based on the information provided on sites, that didn't worked. We tried various options on pseudo header by including and not including source address, destination address. But nothing worked.
    Kindly note that ping and traceroute are working fine from command line using ping and tracert on Win XP / Win 2003 in IPv6 environment. While going through some sites and forums I came to know that IPv6 is not completely implemented on Win XP or Win Server 2003. Is it causing this problem?

    The issues I guess here are,

    * Checksum calculation have some issues
    * Pseudo-header format and its size that we mentioned may be incorrect
    * IPv6 may not work on Win XP / Win 2003. If this is the case, how to get this one work on Win XP and Win 2003 (both 32 and 64 bits)

    The following is the part of source code that fills ICMP data and calculate checksum.

    Code:

    Code:
    // ICMP packet types
    #define ICMP_ECHO_REPLY 0
    #define ICMP_ECHO_REPLY_V6 129
    
    #define ICMP_DEST_UNREACH 3
    #define ICMP_DEST_UNREACH_V6 1
    
    #define ICMP_TTL_EXPIRE 11
    #define ICMP_TTL_EXPIRE_V6 3
    
    #define ICMP_ECHO_REQUEST 8
    #define ICMP_ECHO_REQUEST_V6 128
    
    // ICMP header structure
    typedef struct _icmpHdr
    {
    unsigned char i_type; // ICMP packet type
    unsigned char i_code; // Sub code type
    unsigned short i_checksum; // ICMP Checksum
    unsigned short i_id; // Process Id
    unsigned short i_seq; // ICMP Sequence number
    //
    // This is not the std header, but we reserve space for time
    //
    unsigned long i_ulTimeStamp;
    } ICMPHeader;
    
    // Fill in ICMP Packet field
    void ping::InitICMPData(char* cHdrPing, int nPacketSize, int nSeqNo, struct addrinfo* dest)
    {
    ICMPHeader *icHdrPing;
    char *datapart;
    int family = dest->ai_family ;
    
    if(AF_INET6 == family)
    {
    //memcpy( cHdrPing + 16 , (void *)&(((struct sockaddr_in6 *)dest->ai_addr)->sin6_addr.u) , 16);
    *(cHdrPing + 32) = 40 ;
    *(cHdrPing + 39) = 58;
    icHdrPing = (ICMPHeader*)(cHdrPing + 40);
    nPacketSize += 40;
    }
    else
    {
    icHdrPing = (ICMPHeader*)cHdrPing;
    }
    
    icHdrPing->i_type = AF_INET6 == family ? ICMP_ECHO_REQUEST_V6:ICMP_ECHO_REQUEST;
    icHdrPing->i_code = 0;
    icHdrPing->i_checksum = 0;
    
    icHdrPing->i_id = htons(whatIsProcessId());
    icHdrPing->i_seq = htons(nSeqNo);
    icHdrPing->i_ulTimeStamp = GetTickCount ();
    
    datapart = (char *)icHdrPing + sizeof(ICMPHeader);
    
    memset(datapart,'E', 32);
    // Calculate a checksum on the result
    icHdrPing->i_checksum = CalcCheckSum((unsigned short*)cHdrPing, nPacketSize);
    printf("TYPE = %d\n" , icHdrPing->i_type);
    printf("CHECk = %x\n" , icHdrPing->i_checksum);
    
    printf("End of InitICMPdata\n");
    }
    
    // Calculate check sum of the packet
    unsigned short ping::CalcCheckSum(unsigned short* usBuffer, int nSize)
    {
    unsigned long ulChkSum = 0;
    
    while(nSize > 1)
    {
    ulChkSum += *usBuffer++;
    nSize -= sizeof(unsigned short);
    }
    
    if (nSize)
    {
    ulChkSum += *(unsigned char*)usBuffer;
    }
    
    ulChkSum = (ulChkSum >> 16) + (ulChkSum & 0xffff);
    ulChkSum += (ulChkSum >> 16);
    
    return (unsigned short)(~ulChkSum);
    }
    As it is a very critical reqt. we have to fix this problem immediately. Please help me find a solution on this issue to get this work on Win XP / Win 2003.

    Thanks in advance.


    Regards,
    Vignesh

  2. #2
    int x = *((int *) NULL); Cactus_Hugger's Avatar
    Join Date
    Jul 2003
    Location
    Banks of the River Styx
    Posts
    902
    RFC 4443 - Internet Control Message Protocol (ICMPv6) for the Internet Protocol Version 6 (IPv6) Specification
    Read 2.3 - your checksum function does not seem to match what is written there.
    long time; /* know C? */
    Unprecedented performance: Nothing ever ran this slow before.
    Any sufficiently advanced bug is indistinguishable from a feature.
    Real Programmers confuse Halloween and Christmas, because dec 25 == oct 31.
    The best way to accelerate an IBM is at 9.8 m/s/s.
    recursion (re - cur' - zhun) n. 1. (see recursion)

  3. #3
    Registered User
    Join Date
    Apr 2009
    Location
    Bangalore
    Posts
    6
    Thanks for the response Cactus.

    if you look into the InitICMPData(char* cHdrPing, int nPacketSize, int nSeqNo, struct addrinfo* dest) method you will find that it is matching RFC 4443. What I am trying to do there is fill the pseudo header, put ICMP header data and then I am trying to pass those data for calculating checksum. Based on the pseudo header information provided in RFC, i copy values in the case of IPv6 as follows. I have provided comments inline in the code.

    Code:
    ICMPHeader *icHdrPing;
    char *datapart;
    int family = dest->ai_family ;
    
    if(AF_INET6 == family)
    {
    // Pseudo header starts here
    // Here I copy destination address
    memcpy( cHdrPing + 16 , (void *)&(((struct sockaddr_in6 *)dest->ai_addr)->sin6_addr.u) , 16);
    // Upper layer packet length is provided here
    *(cHdrPing + 32) = 40 ;
    // This is next header
    *(cHdrPing + 39) = 58;
    // Copying cHdrPing from 40th location into icHdrPing
    icHdrPing = (ICMPHeader*)(cHdrPing + 40);
    nPacketSize += 40;
    }
    else
    {
    icHdrPing = (ICMPHeader*)cHdrPing;
    }
    
    // Now filling ICMPv6 header data
    icHdrPing->i_type = AF_INET6 == family ? ICMP_ECHO_REQUEST_V6:ICMP_ECHO_REQUEST;
    icHdrPing->i_code = 0;
    // Initially setting checksum to 0
    icHdrPing->i_checksum = 0;
    icHdrPing->i_id = htons(whatIsProcessId());
    icHdrPing->i_seq = htons(nSeqNo);
    icHdrPing->i_ulTimeStamp = GetTickCount ();
    
    datapart = (char *)icHdrPing + sizeof(ICMPHeader);
    
    memset(datapart,'E', 32);
    // Now calculating checksum for the pseudo header + ICMP header. cHdrPing variable have those data in it. Checksum is now calculated for that data here
    icHdrPing->i_checksum = CalcCheckSum((unsigned short*)cHdrPing, nPacketSize);
    printf("TYPE = %d\n" , icHdrPing->i_type);
    printf("CHECk = %x\n" , icHdrPing->i_checksum);
    
    printf("End of InitICMPdata\n");
    }
    I assume some issues could be there in pseudo header I am filling in the memory. I tried identifying the issue but without any success. Please go through the above code with comments and let me know if I am doing anything wrong.
    Last edited by vigneshp; 04-28-2009 at 06:58 AM.

  4. #4
    int x = *((int *) NULL); Cactus_Hugger's Avatar
    Join Date
    Jul 2003
    Location
    Banks of the River Styx
    Posts
    902
    Ah, I was misinterpreting your code. Ok then, this section:
    Code:
    // Upper layer packet length is provided here
    *(cHdrPing + 32) = 40 ;
    // This is next header
    *(cHdrPing + 39) = 58;
    // Copying cHdrPing from 40th location into icHdrPing
    icHdrPing = (ICMPHeader*)(cHdrPing + 40);
    First - the last line seems to indicate that the ICMPv6 data is at cHdrPing + 40, and that the IPv6 header is at cHdrPing (correct me if I'm wrong). If this is true, and cHdrPing should point to the start of an IPv6 packet, then you're not setting the next header and the upper packet length correctly. Specifically, these:
    Code:
    // Upper layer packet length is provided here
    *(cHdrPing + 32) = 40 ;
    // This is next header
    *(cHdrPing + 39) = 58;
    ...would be setting two bytes inside the destination address. See Wikipedia's article on IPv6 for a detail of the header - not that the numbers in the diagram are in bits (I highly suspect this is where you're off...).

    This section also bothers me:
    Code:
    datapart = (char *)icHdrPing + sizeof(ICMPHeader);
    
    memset(datapart,'E', 32);
    You said in the IPv6 header that your ICMP was 40 octets, yet here, we claim it to be sizeof(ICMPHeader) + 32 = 44 octets, due to the non-standard long you added to your structure.
    long time; /* know C? */
    Unprecedented performance: Nothing ever ran this slow before.
    Any sufficiently advanced bug is indistinguishable from a feature.
    Real Programmers confuse Halloween and Christmas, because dec 25 == oct 31.
    The best way to accelerate an IBM is at 9.8 m/s/s.
    recursion (re - cur' - zhun) n. 1. (see recursion)

  5. #5
    Registered User
    Join Date
    Apr 2009
    Location
    Bangalore
    Posts
    6
    Have a look into this link that shows pseudo-header format, the one I followed while implementation. Here is the link:

    Tech Info - IPv6

    Based on the above link, I provide 16 bytes for source address, another 16 bytes for destination address, packet length is 4 bytes, other 3 bytes is 0 and finally next header is 1 byte. I follow the same and you can see it in my code. I guess it is right.

    Now I set 44 octets for upper layer packet length as you mentioned in your response. Please respond.


    Thanks,
    Vignesh

  6. #6
    int x = *((int *) NULL); Cactus_Hugger's Avatar
    Join Date
    Jul 2003
    Location
    Banks of the River Styx
    Posts
    902
    I'm not sure my previous point made it across...
    This:
    Code:
    // This is next header
    *(cHdrPing + 39) = 58;
    If cHdrPing points to a buffer that is an IPv6 packet, then you are not setting the IPv6 Next Header correctly. 39 bytes inside of an IPv6 packet is the very last byte of the IPv6 header - the last byte of the destination address, not the next header. The fact that you set this byte to 58 (the next header value for ICMPv6) makes me even more suspicious that this is your error. The link you posted echos this information - note that it is measuring fields in bits but this:
    Code:
    // This is next header
    *(cHdrPing + 39) = 58;
    ...is an offset of 39 bytes.
    Am I going wrong somewhere?
    long time; /* know C? */
    Unprecedented performance: Nothing ever ran this slow before.
    Any sufficiently advanced bug is indistinguishable from a feature.
    Real Programmers confuse Halloween and Christmas, because dec 25 == oct 31.
    The best way to accelerate an IBM is at 9.8 m/s/s.
    recursion (re - cur' - zhun) n. 1. (see recursion)

  7. #7
    Registered User
    Join Date
    Apr 2009
    Location
    Bangalore
    Posts
    6
    Hi Cactus,
    I understood from your post that you are asking me to set IPv6 header before ICMPv6 header. Correct me if I am wrong.
    But ICMPv6 RFC tell that I have to put pseudo-header before ICMP buffer. Please go through the following ICMPv6 RFC.

    RFC 2463 (rfc2463) - Internet Control Message Protocol (ICMPv6) for the In

    There is lot of difference between pseudo-header and IPv6 header format. In the case of ICMPv6 we just need to put pseudo-header. The format is,

    Source address - 128 bits
    Destination address - 128 bits
    Packet length - 32 bits
    0 is 24 bits
    Next header - 8 bits

    But IPv6 header format is,

    Version - 4 bits
    Traffic class - packet priority - 8 bits
    Flow label - 20 bits
    Payload length - 16 bits
    Next header - 8 bits
    Hop limit - 8 bits
    Source and destination addresses - 128 bits each.

    So as per my code, I put next header at 40. I guess i have to put pseudo-header in the place and not IPv6 header. Am I wrong in understanding this. If so, please correct me.

    Also please let me know if there is any link that provide more clarity (with some sample) on calculating checksum in IPv6.

    Thanks,
    Vignesh

  8. #8
    int x = *((int *) NULL); Cactus_Hugger's Avatar
    Join Date
    Jul 2003
    Location
    Banks of the River Styx
    Posts
    902
    I have done my homework, now, I think. I had always though "pseudo-header" referred to the IPv6 header, with some fields zeroed. Now that I've got the correct RFC in hand, I see it is otherwise. (Why they put information pertaining to ICMPv6 in the IPv6 RFC is still beyond me, as is to why they decided to lay out the pseudo-header the way they did - no rational is provided as to why that format is better than the original IPv6 format...)

    After doing some tests on some ICMPv6 echo packets I captured, it would seem that you need to fill in source & destination addresses in the pseudo-header. Your entry of the next header value in the pseudo-header seems to be correct, however, I am not sure that your payload length is - offset 32 in the pseudo header is indeed the first byte of the length, but also the most significant - so, you're entering 0x40000000 as the packet's payload length.

    I also still doubt your length calculation, as I mentioned earlier. This line is what troubles me:
    Code:
    datapart = (char *)icHdrPing + sizeof(ICMPHeader);
    
    memset(datapart,'E', 32);
    That's 32 'E's, plus sizeof(ICMPHeader), which is 12, which equals 44 bytes total.

    Finally, your one's complement code:
    Code:
    	ulChkSum = (ulChkSum >> 16) + (ulChkSum & 0xffff);
    	ulChkSum += (ulChkSum >> 16);
    
    	return (unsigned short)(~ulChkSum);
    }
    The first line, OK - add in the carries. But that second line? Should we not also do a (ulChkSum & 0xffff) here? In my trying to duplicate the checksum of a captured wireshark ICMPv6 packet, I used:
    Code:
    while(sum > 0xFFFFUL)
    {
    	sum = (sum & 0xFFFF) + (sum >> 16);
    }
    long time; /* know C? */
    Unprecedented performance: Nothing ever ran this slow before.
    Any sufficiently advanced bug is indistinguishable from a feature.
    Real Programmers confuse Halloween and Christmas, because dec 25 == oct 31.
    The best way to accelerate an IBM is at 9.8 m/s/s.
    recursion (re - cur' - zhun) n. 1. (see recursion)

  9. #9
    Registered User
    Join Date
    Apr 2009
    Location
    Bangalore
    Posts
    6
    Hi Cactus,

    I have identified the problem, fixed it and is working fine. Thanks a lot for your time spend on this issue. The problem is in payload size that I am putting in pseudo-header. The payload size variable size is 4 bytes (i.e) 32nd byte of pseudo-header. I was placing payload size at 32nd position, but it should be at 35th position (that is at 4th byte). That means the value should be stored in big endian order. Also another issue I was facing with source address. Initially i was not filling it. After filling the source address, it is able to ping and traceroute IPv6 on both Xp and Win2k3.

    Thanks again..

    - Vignesh P

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. IPv6 ping in windows...problem..lots of ode:(
    By Neill KElly in forum C Programming
    Replies: 3
    Last Post: 04-27-2009, 11:50 PM

Tags for this Thread