Search code examples
network-programmingip

How to parse an IP Packet stored in char buffer in C/C++


I have an IP packet stored in a char buffer:

char packet[] = "450000CC68C4000001114C4F0A0A0A0AEFFFFFFADEA9076C00B832074D2D534541524348202A20485454502F312E310D0A484F53543A203233392E3235352E3235352E3235303A313930300D0A4D414E3A2022737364703A646973636F766572220D0A4D583A20310D0A53543A2075726E3A6469616C2D6D756C746973637265656E2D6F72673A736572766963653A6469616C3A310D0A555345522D4147454E543A20476F6F676C65204368726F6D652F3130372E302E353330342E313130204D6163204F5320580D0A0D0A"

I am try to parse this IP header using netinet/ip.h ip definition.

But when i try to print the ip packet details like ip header length, it is printing garbage:

#include <netinet/ip.h>

    void decodeIPPacket(const char *packet)
    {
        struct ip *ipHdr = (struct ip*)(long)packet;
        
        printf("decodeIPPacket:: IP Len: %d", ipHdr->ip_len);
        printf("decodeIPPacket:: srcip: %u", ipHdr->ip_src.s_addr);
        printf("decodeIPPacket:: dstip: %u", ipHdr->ip_dst.s_addr);
    }

decodeIPPacket:: IP Len: 12336

decodeIPPacket:: srcip: 808464432

decodeIPPacket:: dstip: 825307440

What am i doing wrong here?


Solution

  • Every character in your string represent a nybble in hexadecimal. You need to convert every 2 char from ASCII to hexadecimal as a byte.

    #include <netinet/ip.h>
    #include <stdio.h>
    #include <string.h>
    
    char string[] =
        "450000CC68C4000001114C4F0A0A0A0AEFFFFFFADEA9076C00B832074D2D53454152434820"
        "2A20485454502F312E310D0A484F53543A203233392E3235352E3235352E3235303A313930"
        "300D0A4D414E3A2022737364703A646973636F766572220D0A4D583A20310D0A53543A2075"
        "726E3A6469616C2D6D756C746973637265656E2D6F72673A736572766963653A6469616C3A"
        "310D0A555345522D4147454E543A20476F6F676C65204368726F6D652F3130372E302E3533"
        "30342E313130204D6163204F5320580D0A0D0A";
    
    int main() {
        char *pos = string;
        int packetLen = strlen(string) / 2;
        unsigned char packet[packetLen];
        struct sockaddr_in source, dest;
    
        /* Read only 20 bytes (40 nybbles) 
         * to store only IPv4 header.
         * NOTE: IPv4 header is not always 
         * 20 bytes. Can contain 'options' field.
         * You have to extract ihl value first.
         */
        for (size_t i = 0; i < 40; ++i) {
            sscanf(pos, "%2hhx", &packet[i]);
            pos += 2;
        }
    
        struct iphdr *iph = (struct iphdr *)packet;
        memset(&source, 0, sizeof(source));
        source.sin_addr.s_addr = iph->saddr;
    
        memset(&dest, 0, sizeof(dest));
        dest.sin_addr.s_addr = iph->daddr;
    
        printf("IP Version: %d\n", (unsigned int)iph->version);
        printf("IP Header Length: %d Bytes\n", ((unsigned int)(iph->ihl)) * 4);
        printf("Type Of Service: %d\n", (unsigned int)iph->tos);
        printf("IP Total Length: %d  Bytes(Size of Packet)\n",
               ntohs(iph->tot_len));
        printf("Identification: %d\n", ntohs(iph->id));
        printf("TTL: %d\n", (unsigned int)iph->ttl);
        printf("Protocol: %d\n", (unsigned int)iph->protocol);
        printf("Checksum: %d\n", ntohs(iph->check));
        printf("Source Adddress: %.4x\n", source.sin_addr);
    }
    

    Output:

    IP Version: 4
    IP Header Length: 20 Bytes
    Type Of Service: 0
    IP Total Length: 204  Bytes(Size of Packet)
    Identification: 26820
    TTL: 1
    Protocol: 17
    Checksum: 19535
    Source Adddress: a0a0a0a
    Destination Adddress: faffffef