user3210586
user3210586

Reputation: 81

getting ip address of a packet in pcap file

I am programming with 'winpcap', I read a ".pcap" file in my program and after that I want to get the Ip addresses of packets, I've wrote these code for getting ip addresses,here is the piece of my code:

  struct sniff_ip {
        u_char ip_vhl;      /* version << 4 | header length >> 2 */
        u_char 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_char ip_ttl;      /* time to live */
        u_char ip_p;        /* protocol */
        u_short ip_sum;     /* checksum */
        struct in_addr ip_src;
        struct in_addr ip_dst; /* source and dest address */



      struct sniff_tcp {
                u_short th_sport;   /* source port */
                u_short th_dport;   /* destination port */
                u_int32_t th_seq;       /* sequence number */
                u_int32_t th_ack;       /* acknowledgement number */};

and after that I read the file:

while (pcap_next_ex(handler, &header, &packet) >= 0)
    {

        ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);
        tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip);

        printf("src port: %d dest port: %d \n", tcp->th_sport, tcp->th_dport);
        fprintf(fp,"src port: %d dest port: %d \n", tcp->th_sport, tcp->th_dport);

        printf("src address: %s dest address: %s \n",  inet_ntoa(ip->ip_src),  inet_ntoa(ip->ip_dst));
        fprintf(fp,"src address: %s dest address: %s \n",  inet_ntoa(ip->ip_src),  inet_ntoa(ip->ip_dst));

        printf("seq number: %u ack number: %u \n", (unsigned int)tcp-> th_seq, (unsigned int)tcp->th_ack);
        fprintf(fp,"seq number: %u ack number: %u \n", (unsigned int)tcp-> th_seq, (unsigned int)tcp->th_ack);

but the source and Ip addresses are the same!!!and it print the source and destination port incorrect!!what is the problem?what should I do for it?plz help me.

Upvotes: 3

Views: 8125

Answers (5)

user15829861
user15829861

Reputation: 21

First, make sure that the packets do, in fact, have an Ethernet header (do not assume they do!), by doing

if (pcap_datalink(handler) != DLT_EN10MB) {
    fprintf(stderr, "%s is not an Ethernet capture\n",
            {whatever the pathname of the file is});
    exit(2);
}

after you've opened the capture file.

Then, you must make sure the packet really is an IPv4 packet, either by compiling the string "ip" as a filter, with pcap_compile(), and applying that to the file by calling pcap_setfilter() with handler and the compiled filter before you start reading, or by checking it in your read loop:

while (pcap_next_ex(handler, &header, &packet) >= 0) {
    u_short ethertype;
    
    /*
     * For an Ethernet packet, the destination Ethernet
     * address is in bytes 0 through 5, the source Ethernet
     * address is in bytes 6 through 11, and the type/length
     * field is in bytes 12 and 13.
     *
     * It's a big-endian value, so fetch the first byte, at
     * an offset of 12, and put it in the upper 8 bits of
     * the value, and then fetch the second byte, at an offset
     * of 13, and put it in the lower 8 bits of the value.
     */
    ethertype = (packet[12] << 8) | packet[13];
    
    /*
     * Now make sure it's the Ethernet type value for
     * IPv4, which is 0x0800.
     */
    if (ethertype != 0x0800) {
        /*
         * Skip this packet.
         */
        continue;
    }

    /*
     * Now we know it's an IPv4 packet, so process it as such.
     */
    ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);

Note: do NOT use bitfields for the version and header length fields, as there is no guarantee that they will be in the correct order. Instead, calculate the header length using ip_vhl, e.g.

    size_ip = (ip->ip_vhl & 0x0F) * 4;

Upvotes: 2

Rahul Gulia
Rahul Gulia

Reputation: 69

// We can print the ip address for source and destination like this.

while (currpos < InLen){

    pcktcnt++;
    currpos = ftello64(InRaw);
    if (fread((char *) &pckthdr, sizeof(pckthdr), 1, InRaw) != 1) {
        break;
    }

    if (fread((char *) &pcktbuf, pckthdr.caplen, 1, InRaw) != 1) {
            break;
    }

    /* Find stream in file, count packets and get size (in bytes) */
    if( isgetTCPIP(pcktbuf, &size_ip, &size_tcp,fp)){
        /* Simple example code */       
        char srcIp[INET_ADDRSTRLEN];    /*or malloc it later*/
        char dstIp[INET_ADDRSTRLEN];

        inet_ntop(AF_INET, &(ip->ip_src), srcIp,  INET_ADDRSTRLEN);
        inet_ntop(AF_INET, &(ip->ip_dst), dstIp,  INET_ADDRSTRLEN);
        fprintf("packet: %d, Src addr.: %s, Src port #: %d, Dest. addr: %s,  Dest. port #: %d,   \n",pcktcnt, srcIp,  tcp->th_sport, dstIp, tcp->th_dport);
    }  // isgetTCPIP

Upvotes: 0

OOM
OOM

Reputation: 806

inet_ntoa() returns the dots-and-numbers string in a static buffer that is overwritten with each call to the function.

Upvotes: 0

Aleksander Bavdaz
Aleksander Bavdaz

Reputation: 1346

Source and destination ports are in network byte order (big-endian). Use ntohs to get them in the correct byte order for your machine. Same goes for SEQ and ACK, use ntohl for those. The size of the IP header might not always be 20, multiply the value of ip_hdr_len with 4 to get the actual size.

If your compiler supports bitfields you can use them for your IP header declaration to make things easier:

struct sniff_ip {
    u_char ip_hdr_len:4;
    u_char ip_ver:4;

Fixed code:

while (pcap_next_ex(handler, &header, &packet) >= 0) {      
    ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);

    if (ip->ip_p == 6 /* tcp protocol number */) {
        tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + ip->ip_hdr_len * 4);

        u_short srcport = ntohs(tcp->th_sport);
        u_short dstport = ntohs(tcp->th_dport);
        printf("src port: %d dest port: %d \n", srcport, dstport);

        char srcname[100];
        strcpy(srcname, inet_ntoa(ip->ip_src));
        char dstname[100];
        strcpy(dstname, inet_ntoa(ip->ip_dst));
        printf("src address: %s dest address: %s \n", srcname, dstname);

        u_long seq = ntohl(tcp->th_seq);
        u_long ack = ntohl(tcp->th_ack);
        printf("seq number: %u ack number: %u \n", seq, ack);
    }       
}

Note that not every packet will contain TCP data, unless you applied a filter using pcap_compile and pcap_setfilter or can assure that the file you're reading contains only TCP packets. Therefore you might want to check the value of the IP headers protocol field to be 6 (TCP) as seen in the above code.

Also note that Wireshark by default displays relative SEQ and ACK numbers so they will not match with what you see there.

Structure packing should be fine.

Upvotes: 4

Guntram Blohm
Guntram Blohm

Reputation: 9819

How to pack structures depends on your compiler; for example, gcc uses #pragma pack(). Google for pack pragma gcc or pack pragma visual c or whichever compiler you're using.

You need to save the results of inet_ntoa if you're calling it twice to printf, like in

char *srcname=strdup(inet_ntoa(ip->ip_src));
char *dstname=strdup(inet_ntoa(ip->ip_dst));
printf("src address: %s dest address: %s \n", srcname, dstname);
free(srcname);
free(dstname);

(Note that you should add error checking, the function result pointers might be NULL).

Upvotes: 0

Related Questions