Featured Post

Trie implementation in C

Create your own packet sniffer in C

A simple implementation of a packet sniffer in C on linux platform using the libpcap library. This packet sniffer currently sniffs IP , TCP , ICMP and UDP packets. It can be modified to any protocol as needed just by introducing the header information in it.

Certain filters can be used too like port number and specific host etc.
e.g.

Expression
Description
  ip
Capture all IP packets.
  tcp
Capture only TCP packets.
  tcp port 80
Capture only TCP packets with a port equal to 80.
  ip host 10.1.2.3
Capture all IP packets to or from host 10.1.2.3.

It is a little modified version of sniffer from tcpdump website.

Note : To run this code you require root permissions. 

Here's the code:

/*sniffer.c*/
//To compile : gcc -o sniffer sniffer.c -lpcap
//To run : ./sniffer [interface-name]

#include <pcap.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

/* default snap length (maximum bytes per packet to capture) */
#define SNAP_LEN 1518

/* ethernet headers are always exactly 14 bytes [1] */
#define SIZE_ETHERNET 14

/* Ethernet addresses are 6 bytes */
#define ETHER_ADDR_LEN 6

/* Ethernet header */
struct sniff_ethernet {
        u_char  ether_dhost[ETHER_ADDR_LEN];    /* destination host address */
        u_char  ether_shost[ETHER_ADDR_LEN];    /* source host address */
        u_short ether_type;                     /* IP? ARP? RARP? etc */
};

/* IP header */
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,ip_dst;  /* source and dest address */
};
#define IP_HL(ip)               (((ip)->ip_vhl) & 0x0f)
#define IP_V(ip)                (((ip)->ip_vhl) >> 4)

/* TCP header */
typedef u_int tcp_seq;

struct sniff_tcp {
        u_short th_sport;               /* source port */
        u_short th_dport;               /* destination port */
        tcp_seq th_seq;                 /* sequence number */
        tcp_seq th_ack;                 /* acknowledgement number */
        u_char  th_offx2;               /* data offset, rsvd */
#define TH_OFF(th)      (((th)->th_offx2 & 0xf0) >> 4)
        u_char  th_flags;
        #define TH_FIN  0x01
        #define TH_SYN  0x02
        #define TH_RST  0x04
        #define TH_PUSH 0x08
        #define TH_ACK  0x10
        #define TH_URG  0x20
        #define TH_ECE  0x40
        #define TH_CWR  0x80
        #define TH_FLAGS        (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
        u_short th_win;                 /* window */
        u_short th_sum;                 /* checksum */
        u_short th_urp;                 /* urgent pointer */
};

void
got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet);

void
print_payload(const u_char *payload, int len);

void
print_hex_ascii_line(const u_char *payload, int len, int offset);



/*
 * print data in rows of 16 bytes: offset   hex   ascii
 *
 * 00000   47 45 54 20 2f 20 48 54  54 50 2f 31 2e 31 0d 0a   GET / HTTP/1.1..
 */
void
print_hex_ascii_line(const u_char *payload, int len, int offset)
{

 int i;
 int gap;
 const u_char *ch;

 /* offset */
 printf("%05d   ", offset);
 
 /* hex */
 ch = payload;
 for(i = 0; i < len; i++) {
  printf("%02x ", *ch);
  ch++;
  /* print extra space after 8th byte for visual aid */
  if (i == 7)
   printf(" ");
 }
 /* print space to handle line less than 8 bytes */
 if (len < 8)
  printf(" ");
 
 /* fill hex gap with spaces if not full line */
 if (len < 16) {
  gap = 16 - len;
  for (i = 0; i < gap; i++) {
   printf("   ");
  }
 }
 printf("   ");
 
 /* ascii (if printable) */
 ch = payload;
 for(i = 0; i < len; i++) {
  if (isprint(*ch))
   printf("%c", *ch);
  else
   printf(".");
  ch++;
 }

 printf("\n");

return;
}

/*
 * print packet payload data (avoid printing binary data)
 */
void
print_payload(const u_char *payload, int len)
{

 int len_rem = len;
 int line_width = 16;   /* number of bytes per line */
 int line_len;
 int offset = 0;     /* zero-based offset counter */
 const u_char *ch = payload;

 if (len <= 0)
  return;

 /* data fits on one line */
 if (len <= line_width) {
  print_hex_ascii_line(ch, len, offset);
  return;
 }

 /* data spans multiple lines */
 for ( ;; ) {
  /* compute current line length */
  line_len = line_width % len_rem;
  /* print line */
  print_hex_ascii_line(ch, line_len, offset);
  /* compute total remaining */
  len_rem = len_rem - line_len;
  /* shift pointer to remaining bytes to print */
  ch = ch + line_len;
  /* add offset */
  offset = offset + line_width;
  /* check if we have line width chars or less */
  if (len_rem <= line_width) {
   /* print last line and get out */
   print_hex_ascii_line(ch, len_rem, offset);
   break;
  }
 }

return;
}

/*
 * dissect/print packet
 */
void
got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
{

 static int count = 1;                   /* packet counter */
 
 /* declare pointers to packet headers */
 const struct sniff_ethernet *ethernet;  /* The ethernet header [1] */
 const struct sniff_ip *ip;              /* The IP header */
 const struct sniff_tcp *tcp;            /* The TCP header */
 const char *payload;                    /* Packet payload */

 int size_ip;
 int size_tcp;
 int size_payload;
 
 printf("\nPacket number %d:\n", count);
 count++;
 
 /* define ethernet header */
 ethernet = (struct sniff_ethernet*)(packet);
 
 /* define/compute ip header offset */
 ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);
 size_ip = IP_HL(ip)*4;
 if (size_ip < 20) {
  printf("   * Invalid IP header length: %u bytes\n", size_ip);
  return;
 }

 /* print source and destination IP addresses */
 printf("       From: %s\n", inet_ntoa(ip->ip_src));
 printf("         To: %s\n", inet_ntoa(ip->ip_dst));
 
 /* determine protocol */ 
 switch(ip->ip_p) {
  case IPPROTO_TCP:
   printf("   Protocol: TCP\n");
   break;
  case IPPROTO_UDP:
   printf("   Protocol: UDP\n");
   return;
  case IPPROTO_ICMP:
   printf("   Protocol: ICMP\n");
   return;
  case IPPROTO_IP:
   printf("   Protocol: IP\n");
   return;
  default:
   printf("   Protocol: unknown\n");
   return;
 }
 
 /*
  *  OK, this packet is TCP.
  */
 
 /* define/compute tcp header offset */
 tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip);
 size_tcp = TH_OFF(tcp)*4;
 if (size_tcp < 20) {
  printf("   * Invalid TCP header length: %u bytes\n", size_tcp);
  return;
 }
 
 printf("   Src port: %d\n", ntohs(tcp->th_sport));
 printf("   Dst port: %d\n", ntohs(tcp->th_dport));
 
 /* define/compute tcp payload (segment) offset */
 payload = (u_char *)(packet + SIZE_ETHERNET + size_ip + size_tcp);
 
 /* compute tcp payload (segment) size */
 size_payload = ntohs(ip->ip_len) - (size_ip + size_tcp);
 
 /*
  * Print payload data; it might be binary, so don't just
  * treat it as a string.
  */
 if (size_payload > 0) {
  printf("   Payload (%d bytes):\n", size_payload);
  print_payload(payload, size_payload);
 }

return;
}

int main(int argc, char **argv)
{

 char *dev = NULL;   /* capture device name */
 char errbuf[PCAP_ERRBUF_SIZE];  /* error buffer */
 pcap_t *handle;    /* packet capture handle */

 char filter_exp[] = "ip";  /* filter expression */
 struct bpf_program fp;   /* compiled filter program (expression) */
 bpf_u_int32 mask;   /* subnet mask */
 bpf_u_int32 net;   /* ip */
 int num_packets ;   /* number of packets to capture */

 /* check for capture device name on command-line */
 if (argc == 2) {
  dev = argv[1];
 }
 else if (argc > 3) {
  fprintf(stderr, "error: unrecognized command-line options\n\n");
 printf("Usage: %s [interface]\n", argv[0]);
 printf("\n");
 printf("Options:\n");
 printf("    interface    Listen on <interface> for packets.\n");
 printf("\n");
  exit(EXIT_FAILURE);
 }
 else {
  /* find a capture device if not specified on command-line */
  dev = pcap_lookupdev(errbuf);
  if (dev == NULL) {
   fprintf(stderr, "Couldn't find default device: %s\n",
       errbuf);
   exit(EXIT_FAILURE);
  }
 }
 printf("\nEnter no. of packets you want to capture: ");
        scanf("%d",&num_packets);
        printf("\nWhich kind of packets you want to capture : ");
        scanf("%s",filter_exp);
 /* get network number and mask associated with capture device */
 if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) {
  fprintf(stderr, "Couldn't get netmask for device %s: %s\n",
      dev, errbuf);
  net = 0;
  mask = 0;
 }

 /* print capture info */
 printf("Device: %s\n", dev);
 printf("Number of packets: %d\n", num_packets);
 printf("Filter expression: %s\n", filter_exp);

 /* open capture device */
 handle = pcap_open_live(dev, SNAP_LEN, 1, 1000, errbuf);
 if (handle == NULL) {
  fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
  exit(EXIT_FAILURE);
 }

 /* make sure we're capturing on an Ethernet device [2] */
 if (pcap_datalink(handle) != DLT_EN10MB) {
  fprintf(stderr, "%s is not an Ethernet\n", dev);
  exit(EXIT_FAILURE);
 }

 /* compile the filter expression */
 if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1) {
  fprintf(stderr, "Couldn't parse filter %s: %s\n",
      filter_exp, pcap_geterr(handle));
  exit(EXIT_FAILURE);
 }

 /* apply the compiled filter */
 if (pcap_setfilter(handle, &fp) == -1) {
  fprintf(stderr, "Couldn't install filter %s: %s\n",
      filter_exp, pcap_geterr(handle));
  exit(EXIT_FAILURE);
 }

 /* now we can set our callback function */
 pcap_loop(handle, num_packets, got_packet, NULL);

 /* cleanup */
 pcap_freecode(&fp);
 pcap_close(handle);

 printf("\nCapture complete.\n");

return 0;
}


Comments

  1. Hello, your code is very intersted, but when I tryed to compile, this is de uot put with gcc.

    [root@localhost pruebas]# cc sniffer.c -o sniffer
    /tmp/ccnkWrfQ.o: In function `main':
    sniffer.c:(.text+0x4a3): undefined reference to `pcap_lookupdev'
    sniffer.c:(.text+0x549): undefined reference to `pcap_lookupnet'
    sniffer.c:(.text+0x5f8): undefined reference to `pcap_open_live'
    sniffer.c:(.text+0x64d): undefined reference to `pcap_datalink'
    sniffer.c:(.text+0x6ae): undefined reference to `pcap_compile'
    sniffer.c:(.text+0x6c2): undefined reference to `pcap_geterr'
    sniffer.c:(.text+0x708): undefined reference to `pcap_setfilter'
    sniffer.c:(.text+0x71c): undefined reference to `pcap_geterr'
    sniffer.c:(.text+0x772): undefined reference to `pcap_loop'
    sniffer.c:(.text+0x77e): undefined reference to `pcap_freecode'
    sniffer.c:(.text+0x78d): undefined reference to `pcap_close'
    collect2: ld devolvió el estado de salida 1

    ReplyDelete
    Replies
    1. You need to install the libpcap-dev package in your system.

      Delete
    2. Hi, Varun.

      Can you provide some sample input?

      May i ask what is the interface-name?

      How to change the root permissions to run this code?
      here are the output said that i don't have the permission to run the code
      "You don't have permission to capture on that device (socket: Operation not permitted)"

      Thank you very much. Hope to get ur reply soon.

      Delete
    3. You can use sudo to run this program:

      e.g. On linux :

      For default ethernet interface - sudo ./sniffer eth0
      For wireless interface - sudo ./sniffer wlan0

      Delete
    4. i am facing same problem n i am using 16.04 pls help me out

      Delete
    5. Please use this command to compile:
      gcc -o sniffer sniffer.c -lpcap

      Ensure you have libpcap-dev installed on your system

      Delete
  2. your code is very very nice, the way that I have compiled it is the following "gcc sniffer.c -lpcap -o sniffer" Thank you.

    ReplyDelete
    Replies
    1. That's great .. I think I forgot to add the linker option :)

      Delete
  3. I compiled the code and when I had to run it I didn't know what to write for Interfacename

    ReplyDelete
  4. Thanks!!
    Works perfect.. everything.
    Working on an IDS... had to implement packet analyser... Thank you!! :)

    ReplyDelete
    Replies
    1. Bisa minta tolong?
      hubungi saya via defri.idris@gmail.com
      saya sedang mengerjakan projek IDS juga.

      Delete
  5. can u please upload the output of this program ,just to compare with my results

    ReplyDelete
    Replies
    1. Hi Archana,
      The output will differ on each run on different networks. It's a packet sniffer and the output depends on the packets captured on that network. It's not a fixed output program.

      Delete
  6. Awesome code bro. thanks a tonne!!

    ReplyDelete
  7. Thanks for the code.
    I tried this code ,it works fine with ip expression
    but it gives something like as below for tcp expression
    Device: eth0
    Number of packets: 10
    Filter expression: tcp��
    Couldn't parse filter tcp���: illegal token: ��

    ReplyDelete
    Replies
    1. Hi Shyam,

      It seems that the filter expression has some special characters. The code doesn't handle such scenarios. The expression has to be the correct one. Please check once more if anything else apart from tcp was given.

      Delete
  8. What to type on asking "Which kind of packets you want to capture?"

    ReplyDelete
    Replies
    1. You have to provide the filter expression. For example if you want to capture all TCP packets on port 80, the expression would be :

      tcp port 80

      Or if you want to capture all IP packets from or to a particular host, the expression would be :

      ip host

      Delete
  9. i need a help
    hw to run this?? Please help me

    ReplyDelete
    Replies
    1. The details to run this program is present at the top of the program

      Delete
  10. hi varun,
    when i was running this code it is giving an error regarding pcap header
    saying that"pcap.h:no such file or directory found"

    ReplyDelete
    Replies
    1. You need to install libpcap library. In case you are using Ubuntu, use:

      sudo apt-get install libpcap-dev

      Delete
  11. Hey varun..thanks for the code!!
    But we are getting an error which says couldn't get netmask for device:no IPv4 assigned
    Can you please help us with that!

    ReplyDelete
  12. While I was trying to compile the code, I've got an error about

    ----------------------------------------------------------------------------------------------
    invalid conversion from ‘u_char* {aka unsigned char*}’ to ‘const char*’ [-fpermissive]
    payload = (u_char *)(packet + SIZE_ETHERNET + size_ip + size_tcp);
    ----------------------------------------------------------------------------------------------

    Finally I've fixed this by changing the line 198 to this:
    const u_char *payload;_}

    It works great. Good job.

    ReplyDelete
  13. the program halts after displaying the filter expression
    can you help?

    ReplyDelete
  14. Will it work on windows? or just linux

    ReplyDelete
  15. Every nice code, I just wanted to ask you if it is possible to utilize the DPI library and get the type right away?

    ReplyDelete
  16. Thank you for the code. is it possible to assign the code to capture all without filter?

    ReplyDelete
  17. Hi,

    I want to capture a random tcp packet.
    But, my program has to run as an ordinary user.
    Please let me know how to do this?

    Regards,
    Narendra

    ReplyDelete
  18. You have potential Varun. If you can give a new touch to your current website where source code for simplescoding programs are easily viewable, you can potentially have a million hits and reach to benefit a wider audience (+have it up in Google rankings - as I don't even remember how I got here) ;)

    ReplyDelete
  19. Hi my code is not working
    passing 'const char *' to parameter of type 'const u_char *' (aka 'const unsigned char *') converts between pointers to integer types where one is of the unique plain 'char' type and the other is not [-Wpointer-sign]

    ReplyDelete

Post a Comment

Please post your valuable suggestions