Thread: Return value from recvfrom is different from ip_len

  1. #1
    Registered User
    Join Date
    Apr 2008
    Posts
    8

    Return value from recvfrom is different from ip_len

    Hello

    I'm facing a very confusing issue. I'm working on a sockets program and am faced with the following issue.

    The value returned from the call to recvfrom is 84 bytes. However the value in ip_len is 64 bytes!!!!! The difference in 20 bytes seems to be the IP header length.

    The very confusing issue is that a capture using tcpdump shows the following output

    Code:
    0x0000:  4500 0054 c6fb 0000 3601 bd7c d183 249f
    0x0010:  0a00 000f 0000 5fce 1557 0001 d0f2 94e5
    0x0020:  8abc af41 0809 0a0b 0c0d 0e0f 1011 1213
    0x0030:  1415 1617 1819 1a1b 1c1d 1e1f 2021 2223
    0x0040:  2425 2627 2829 2a2b 2c2d 2e2f 3031 3233
    0x0050:  3435 3637
    Where as printing the packet in userspace shows the following output

    Code:
    0x0000:  4500 4000 C6FB 0000 3601 BD7C D183 249F 
    0x0010:  0A00 000F 0000 5FCE 1557 0001 D0F2 94E5 
    0x0020:  8ABC AF41 0809 0A0B 0C0D 0E0F 1011 1213 
    0x0030:  1415 1617 1819 1A1B 1C1D 1E1F 2021 2223 
    0x0040:  2425 2627 2829 2A2B 2C2D 2E2F 3031 3233 
    0x0050:  3435 3637
    The packets are IDENTICAL except for the 2 red bolded bytes

    Can someone please tell me what is going on

    Thanks

    Cheers

  2. #2
    Registered User
    Join Date
    Sep 2004
    Location
    California
    Posts
    3,268
    The data length was modified from big endian to little endian format, and the size of the IP header (20 bytes) was subtracted from it.

    0x0054 in big endian = 84.
    0x4000 in little endian = 64.

    As for why the conversion took place, I'm not sure. The first packet you printed (the one from tcpdump) is correct. The size field should include the data plus the header, and it should be in big endian format. How are you printing out the packet in the user space? Maybe something you are doing to print it out is causing the discrepancy.

    The value returned from the call to recvfrom is 84 bytes. However the value in ip_len is 64 bytes
    You got this backwards. The value in ip_len is 84 bytes, and recvfrom is returning 64 bytes. This is because recvfrom returns the size of the payload, not the size of the entire packet. Therefore it will not include the header size.

  3. #3
    Registered User
    Join Date
    Apr 2008
    Posts
    8
    Quote Originally Posted by bithub View Post
    How are you printing out the packet in the user space? Maybe something you are doing to print it out is causing the discrepancy.

    Code:
    for (int i = 0; i < packetLength; i++)
    {
        char buffer[1];
        sprintf(buffer, %02X, packet[i]);
        printf("%s", buffer);
        if (i % 2)
            printf(" ");
        if (i % 16 == 15)
            printf("\n");
    }
    printf("\n");
    Quote Originally Posted by bithub View Post
    You got this backwards. The value in ip_len is 84 bytes, and recvfrom is returning 64 bytes. This is because recvfrom returns the size of the payload, not the size of the entire packet. Therefore it will not include the header size.
    No, I'm positive about this. The value returned from recvfrom is 84, whereas the value in ip_len is 64.

    After scouring google I seem to have found the issue as detailed in the following thread
    Foundry27 : Post

    What I am unable to figure out is how to identify whether the packet has been passed to rip_input() or not.

    Thanks a lot for your reply mate

    Cheers

  4. #4
    Registered User
    Join Date
    Sep 2004
    Location
    California
    Posts
    3,268
    Ah, I didn't realize you were using RAW sockets. In that case it's a whole different beast. I don't have much experience using RAW sockets; just STREAM and DGRAM. Hopefully someone else here can help you out.

  5. #5
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by s_Fanous View Post
    The very confusing issue is that a capture using tcpdump shows the following output
    tcpdump uses libpcap. Perhaps libpcap is frobbing the data in some way that it shouldn't? I doubt it.

    Where as printing the packet in userspace shows the following output
    What do you mean by "in userspace?" Are you using a SOCK_RAW socket to read packets directly? Then I believe that result, and I do not believe tcpdump's result.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  6. #6
    Registered User
    Join Date
    Sep 2004
    Location
    California
    Posts
    3,268
    Then I believe that result, and I do not believe tcpdump's result.
    It can only be correct if he managed to receive a packet that is 16,384 bytes in size. In this case, I believe tcpdump has the correct result.

  7. #7
    int x = *((int *) NULL); Cactus_Hugger's Avatar
    Join Date
    Jul 2003
    Location
    Banks of the River Styx
    Posts
    902
    Given that the OP's code has the good ole undefined behaviour in it, I think we should concentrate on that before looking at it's output. This section:
    Code:
        char buffer[1];
        sprintf(buffer, %02X, packet[i]);
        printf("%s", buffer);
    buffer is _way_ too small there. It needs to be at least 3 chars long.

    Also, OP: is your program small enough to post? I'd like to see a bit more of it, and if possible, run it locally.

    Edit: Man, my eyes are not reading today. That code segment won't even compile. OP: post what you actually have...
    Last edited by Cactus_Hugger; 06-09-2009 at 04:49 PM.
    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)

  8. #8
    Registered User
    Join Date
    Apr 2008
    Posts
    8
    Quote Originally Posted by Cactus_Hugger View Post
    Given that the OP's code has the good ole undefined behaviour in it, I think we should concentrate on that before looking at it's output. This section:
    Code:
        char buffer[1];
        sprintf(buffer, %02X, packet[i]);
        printf("%s", buffer);
    buffer is _way_ too small there. It needs to be at least 3 chars long.

    Also, OP: is your program small enough to post? I'd like to see a bit more of it, and if possible, run it locally.

    Edit: Man, my eyes are not reading today. That code segment won't even compile. OP: post what you actually have...
    Sorry for the late reply

    I'm writing a ping application for the iPhone . However I'm not using SOCK_RAW as this requires root privilege. I'm using SOCK_DGRAM and IPPROTO_ICMP.

    I can't post the full source code as I've encapsulated most C calls in Objective-C methods of Classes, so it's quite a few number of files.

    Here's a bigger snapshot though

    Code:
    struct sockaddr_in sourceAddressStructure;
    socklen_t sourceAddressStructureLength = sizeof(sourceAddressStructure);
    bzero(&sourceAddressStructure, sourceAddressStructureLength);
    
    unsigned char packet[IP_MAXPACKET];
    int packetLength = [self->socket receiveFrom:packet
    length:IP_MAXPACKET
    flags:0
    source:(struct sockaddr *)&sourceAddressStructure
    sourceLength:&sourceAddressStructureLength];
    		
    printf("Packet Length = %d\n", packetLength); # This prints 84
    		
    for (int i = 0; i < packetLength; i++)
    {
    	char buf[1];
    	sprintf(buf, "%02X", packet[i]);
    	printf("%s", buf);
    	if (i % 2)
    		printf(" ");
    	if (i % 16 == 15)
    		printf("\n");
    }
    printf("\n");
    		
    struct ip *test = (struct ip *)packet;
    printf("Test Version = %d\n", test->ip_v); // This prints 4
    printf("Test Header Length = %d\n", test->ip_hl); // This prints 5
    printf("Test TOS = %d\n", test->ip_tos); // This prints 0
    printf("Test Length = %d\n", (u_short)test->ip_len); // This prints 64!!!!!!!
    printf("Test Identification = %d\n", test->ip_id);
    printf("Test Offset = %d\n", test->ip_off);
    printf("Test TTL = %d\n", test->ip_ttl);
    printf("Test Protocol = %d\n", test->ip_p); // This prints 1
    printf("Test Checksum = %d\n", test->ip_sum);
    printf("Test Source Address = %s\n", inet_ntoa(test->ip_src));
    printf("Test Destination Address = %s\n", inet_ntoa(test->ip_dst));
    P.S. I'm not showing any of the error handling here as it's all being handled by a try block in another file.

    Hope this helps a bit more

    Thanks again and sorry for the delay

    Cheers
    Last edited by s_Fanous; 06-10-2009 at 01:25 AM.

  9. #9
    int x = *((int *) NULL); Cactus_Hugger's Avatar
    Join Date
    Jul 2003
    Location
    Banks of the River Styx
    Posts
    902
    Code:
    int packetLength = [self->socket receiveFrom:packet
    length:IP_MAXPACKET
    flags:0
    source:(struct sockaddr *)&sourceAddressStructure
    sourceLength:&sourceAddressStructureLength];
    For those like me who were wondering, this is an Objective-C function call. If I'm reading it right, then it is roughly equivalent to (in psuedo-C++...):
    Code:
    int packetLength = this->socket->receiveFrom(packet, IP_MAXPACKET, 0, (struct sockaddr *) &sourceAddressStructure, &sourceAddressStructureLength);
    First - that buffer overflow is still present. 'buf' is too small to hold what you're putting in it.
    Second - this line:
    Code:
    struct ip *test = (struct ip *)packet;
    You'll run into deserialization issues if there is any padding in struct ip or if the endianness of the iPhone does not match that of your packet.
    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)

  10. #10
    Registered User
    Join Date
    Apr 2008
    Posts
    8
    Quote Originally Posted by Cactus_Hugger View Post
    First - that buffer overflow is still present. 'buf' is too small to hold what you're putting in it.
    I changed the buffer to 255, same results.

    Quote Originally Posted by Cactus_Hugger View Post
    Second - this line:
    Code:
    struct ip *test = (struct ip *)packet;
    You'll run into deserialization issues if there is any padding in struct ip or if the endianness of the iPhone does not match that of your packet.
    I'm not sure what you mean exactly. Actually I should say that don't have a lot of experience with sockets programming and was very confused about this endianess.

    My understanding is to do the following:

    When preparing a packet header for sending any field whose size is 1 byte or less do nothing about it ip_v, ip_hl, ip_p. Any field whose size is 2 bytes, pass it to htons() ip_len, ip_off. Any field whose size is 4 bytes, pass it to htonl() ip_id.

    When receiving a packet do the opposite. So ntohs() to ip_len and ip_off. ntohl() to ip_id.

    Is my understanding correct or not?

    Thanks mate

    Cheers
    Last edited by s_Fanous; 06-10-2009 at 06:47 AM.

  11. #11
    Registered User
    Join Date
    Sep 2004
    Location
    California
    Posts
    3,268
    You'll run into deserialization issues if there is any padding in struct ip or if the endianness of the iPhone does not match that of your packet.
    Actually, that's not true. The ip structure was made to be able to cast directly to a packet. Its implementation should look something like:
    Code:
    struct ip
      {
    #if __BYTE_ORDER == __LITTLE_ENDIAN
        unsigned int ip_hl:4;               /* header length */
        unsigned int ip_v:4;                /* version */
    #endif
    #if __BYTE_ORDER == __BIG_ENDIAN
        unsigned int ip_v:4;                /* version */
        unsigned int ip_hl:4;               /* header length */
    #endif
        u_int8_t ip_tos;                    /* type of service */
        u_short ip_len;                     /* total length */
        u_short ip_id;                      /* identification */
        u_short ip_off;                     /* fragment offset field */
    #define IP_RF 0x8000                    /* reserved fragment flag */
    #define IP_DF 0x4000                    /* dont fragment flag */
    #define IP_MF 0x2000                    /* more fragments flag */
    #define IP_OFFMASK 0x1fff               /* mask for fragmenting bits */
        u_int8_t ip_ttl;                    /* time to live */
        u_int8_t ip_p;                      /* protocol */
        u_short ip_sum;                     /* checksum */
        struct in_addr ip_src, ip_dst;      /* source and dest address */
      };
    With all the values being in network byte order.

  12. #12
    Registered User
    Join Date
    Apr 2008
    Posts
    8
    Quote Originally Posted by bithub View Post
    With all the values being in network byte order.
    This still takes me back to my 2 questions

    Why is the value returned from recvfrom different than ip_len, and why is ip_len already in host byte order?

    How can I know if ip_len would be in host byte order or in network byte order?

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. 6 measly errors
    By beene in forum Game Programming
    Replies: 11
    Last Post: 11-14-2006, 11:06 AM
  2. Another weird error
    By rwmarsh in forum Game Programming
    Replies: 4
    Last Post: 09-24-2006, 10:00 PM
  3. Why only 32x32? (OpenGL) [Please help]
    By Queatrix in forum Game Programming
    Replies: 2
    Last Post: 01-23-2006, 02:39 PM
  4. opengl help
    By heat511 in forum Game Programming
    Replies: 4
    Last Post: 04-05-2004, 01:08 AM
  5. opengl code not working
    By Unregistered in forum Windows Programming
    Replies: 4
    Last Post: 02-14-2002, 10:01 PM