≑ Menu

How to Use libwireshark in C Program to Decode Network Packets

Wireshark is an open source network packet analyzer.

It can capture, dissect, and decode various protocols. This helps Linux sysadmin to troubleshoot network issues.

Apart from using wirehshark as a standlone application for debugging network packets, you can also write your own extension or plugin using wireshark libraries for your custom application.

This tutorial explains how to use wireshark libraries to write custom code to debug network packets using a C example program.

The code explains two parts. First, to capture network packets. Second, to decode packets using libwireshark.

As a prerequisite, your system should have both libpcap and wireshark libraries installed.

To capture a packet, refer to How to Perform Packet Sniffing Using Libpcap with C Example Code.

You can also open an existing pcap file using the following api inside your C program:

pd = pcap_open_offline(pcap_path, errbuf);

Wireshark code uses its own dissection engine (epan module library) to dissect the network packets.

The following code shows the necessary steps to initialize it properly.

The functions mentioned below are from the wireshark open source code, which will initialize the packet dissection engine, required data structures, variables, GUID mapping, memory allocation subsystem, registering all the protocol dissector handles, host name lookup, that are necessary for dissection process.

static void initialize_epan(void)
{
  int i;
  e_prefs *prefs;
  char *gpf_path, *pf_path;
  int gpf_open_errno, gpf_read_errno;
  int pf_open_errno, pf_read_errno;

  //set timestamp type
  timestamp_set_type(TS_RELATIVE);

  // This function is called when the program starts, to save whatever credential information
  // we'll need later, and to do other specialized platform-dependent initialization
  init_process_policies();
  
  epan_init(register_all_protocols, register_all_protocol_handoffs,
    NULL, NULL, failure_message, open_failure_message,
    read_failure_message, NULL);
  
  
  // Register all non-dissector modules' preferences.
  prefs_register_modules();

  // Read the preferences file, fill in "prefs", and return a pointer to it, 
  // preference file has information about protocol preferences (e.g. default port)
  prefs = read_prefs(&gpf_open_errno, &gpf_read_errno, &gpf_path,
    &pf_open_errno, &pf_read_errno, &pf_path);
  
  if (gpf_path != NULL) {
    if (gpf_open_errno != 0)
      fprintf(stderr, "Can't open global preferences file \"%s\": %s.\n", pf_path, strerror(gpf_open_errno) );
    
    if (gpf_read_errno != 0)
      fprintf(stderr, "I/O error reading global preferences file " "\"%s\": %s.\n", pf_path, strerror(gpf_read_errno) );
  }

  if (pf_path != NULL) {
    if (pf_open_errno != 0)
      fprintf(stderr, "Can't open your preferences file \"%s\": %s.\n",pf_path, strerror(pf_open_errno));
    
    if (pf_read_errno != 0)
      fprintf(stderr, "I/O error reading your preferences file " "\"%s\": %s.\n", pf_path, strerror(pf_read_errno));
    
    g_free(pf_path);
    pf_path = NULL;

  }

  cleanup_dissection();

  // Initialize the dissection engine
  init_dissection();

  /* Set the given nstime_t to (0,maxint) to mark it as "unset"
   * That way we can find the first frame even when a timestamp
   * is zero */

  nstime_set_unset(&first_ts);
  nstime_set_unset(&prev_cap_ts);
}

The following are some of the helper functions that are used in the above epan_init function, which will help to debug any error condition encountered during the execution of epan_init() function.

The following function will get executed if any read error happens during reading of any configuration file.

static void
read_failure_message(const char *filename, int err)
{
  fprintf(stderr, "An error occurred while reading from the file \"%s\": %s.",
    filename, strerror(err) );
}

The following function will get executed to print the error message.

static void
failure_message(const char *msg_format, va_list ap)
{
  vfprintf(stderr, msg_format, ap);
  fprintf(stderr, "\n");
}

The following function will get executed if epan_init couldn’t open a configuration file:

static void
open_failure_message(const char *filename, int err, gboolean for_writing)
{
  fprintf(stderr, "open error. filename = %s, err = %d, for_writing = %d\n",
    filename, err, for_writing);
}

The following helper function is to fill frame data, when parsing the saved pcap file, one by one all the frames will be examined, framd_data structure will be used to store the captured frame’s data and after that dissection algorithm will be applied on it.

fill_framedata function will be used to populate the frame information in fram_data structure.
void fill_framedata(frame_data *fdata, uint64_t frame_number,
                     const struct pcap_pkthdr *h, int ll_type)
{
  fdata->pfd = NULL;
  fdata->num = frame_number;
  fdata->pkt_len = h->len;
  fdata->cum_bytes  = 0;
  fdata->cap_len = h->caplen;
  fdata->file_off = 0;
  fdata->lnk_t = ll_type;
  fdata->abs_ts.secs = h->ts.tv_sec;
  fdata->abs_ts.nsecs = h->ts.tv_usec*1000;
  fdata->flags.passed_dfilter = 0;
  fdata->flags.encoding = CHAR_ASCII;
  fdata->flags.visited = 0;
  fdata->flags.marked = 0;
  fdata->flags.ref_time = 0;
  fdata->color_filter = NULL;

  if (nstime_is_unset(&first_ts) )
   first_ts = fdata->abs_ts;

  nstime_delta(&fdata->rel_ts, &fdata->abs_ts, &first_ts);

  if (nstime_is_unset(&prev_cap_ts) )
    prev_cap_ts = fdata->abs_ts;

  nstime_delta(&fdata->del_cap_ts, &fdata->abs_ts, &prev_cap_ts);
               fdata->del_dis_ts = fdata->del_cap_ts;
               prev_cap_ts = fdata->abs_ts;
}

The following clear_data function will be used to free the frame_data structure, for the cleanup of frame_data structure instance.

static void clear_fdata(frame_data *fdata)
{
  if (fdata->pfd)
    g_slist_free(fdata->pfd);
}

After initializing the wireshark dissection engine rest of the steps are easy, register this function as callback in pcap_loop API.

static void process_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes)
{
  (void) user;
  
  // declare dissection tree data structure, it will contain all the packet information (all the layers)
  epan_dissect_t *edt;

  //declare the frame_data strcture that will be used in populating frame data
  frame_data fdata;
  
  //pseaudo header 
  union wtap_pseudo_header pseudo_header;
  
  static uint32_t frame_number; /* Incremented each time libpcap gives us a packet */
  
  memset(&pseudo_header, 0, sizeof(pseudo_header) );
  
  frame_number++;
  
  fill_framedata(&fdata, frame_number, h, ll_type);
  
  // get new dissection tree 
  edt = epan_dissect_new(verbose /* create_proto_tree */,
                         verbose /* proto_tree_visible */);
  
  // execute dissection engine on frame data
  epan_dissect_run(edt, &pseudo_header, bytes, &fdata,
                   !verbose ? &cinfo : NULL);
  if (verbose)
    proto_tree_print(edt); //print the packet information

  //free the dissection tree   
  epan_dissect_free(edt);

  // free the frame data 
  clear_fdata(&fdata);

}

proto_tree_print function is available in wireshark code. Follow the How to Perform Packet Sniffing Using Libpcap to understand how to register a callback with pcap_loop

Open a file and copy all these all functions, register above function as callback of pcap_loop function.

After completing all the steps, compile the code and copy the epan folder of wireshark into your working directory, and include all the necessary files.

The following is an example of how to compile this program.

g++ t.cpp -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I. -I../include/ -lpthread -L/home/santosh/proj -lpcap -L/home/santosh/proj  -lwireshark

When you execute this code, it will print all the layers. The output is truncated in the following snippet.

# ./a.out  http.pcap
proto = frame, start = 0, len = 265
  frame.time: "Nov 11, 2014 11:30:43.000000000 IST"
  frame.time_epoch: 1397196043.000000000
  frame.time_delta: 0.000000000
  frame.time_delta_displayed: 0.000000000

proto = eth, start = 0, len = 14
  eth.dst: b1:f1:e1:a1:31:c0
  eth.addr: b1:f1:e1:a1:31:c0
  eth.lg: 0
  eth.src: b1:b1:21:d1:f1:11
  eth.type: 2048

proto = ip, start = 14, len = 20
  ip.version: 4
  ip.hdr_len: 20
  ip.dsfield.dscp: 0
  ip.dsfield.ecn: 0
  ip.len: 251
  ip.id: 20596
  ip.flags.mf: 0

proto = expert, start = 0, len = 0
  expert.message: Bad checksum
  expert.severity: 8388608
  expert.group: 16777216
  ip.src: 10.34.77.109
  ip.addr: 10.34.77.109
  ip.src_host: 10.34.77.109
  ip.host: 10.34.77.109

proto = tcp, start = 34, len = 20
  tcp.srcport: 61945
  tcp.port: 8080
  tcp.stream: 0
  tcp.len: 211
  tcp.seq: 1

Text label: SEQ/ACK analysis
  tcp.analysis.bytes_in_flight: 211

proto = http, start = 54, len = 211
Text label: CONNECT www.google.com:443 HTTP/1.1\r\n

proto = expert, start = 0, len = 0
  expert.message: CONNECT www.google.com:443 HTTP/1.1\r\n
  expert.severity: 2097152
  expert.group: 33554432
  http.request.method: CONNECT
  http.request.uri: www.google.com:443
  http.request.version: HTTP/1.1
  http.host: www.google.com
  
Text label: Proxy-Connection: keep-alive\r\n
  http.user_agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.154 Safari/537.36
Text label: \r\n
  http.request.full_uri: http://www.google.comwww.google.com:443
  http.request: 1
Add your comment

If you enjoyed this article, you might also like..

  1. 50 Linux Sysadmin Tutorials
  2. 50 Most Frequently Used Linux Commands (With Examples)
  3. Top 25 Best Linux Performance Monitoring and Debugging Tools
  4. Mommy, I found it! – 15 Practical Linux Find Command Examples
  5. Linux 101 Hacks 2nd Edition eBook Linux 101 Hacks Book

Bash 101 Hacks Book Sed and Awk 101 Hacks Book Nagios Core 3 Book Vim 101 Hacks Book

Comments on this entry are closed.

  • anonymous December 11, 2014, 2:07 am

    Great article but…could you provide the complete t.cpp source code? πŸ™‚

  • Deniz Gezmis December 11, 2014, 3:13 am

    Great write up. Thanks a bunch.

    -Deniz

  • Bob December 11, 2014, 10:51 am

    Great work. Thanks!!!

  • Jagadeesh December 12, 2014, 8:43 am

    Thanks for sharing. It helped me.

  • Jalal Hajigholamali December 13, 2014, 6:43 am

    Hi,
    Thanks a lot for nice article

  • duskoKoscica December 23, 2014, 1:57 am

    To stay up to date>

    Since C99 it is possible to use restrict as well in situations like this>

    const char *msg_format

  • david June 3, 2015, 9:14 pm

    this is a great share! Could please provide full source code so that we could play with it? Thank you in advance~

  • nathan February 4, 2016, 1:51 pm

    I was digging through tshark code for using the dissectors….this is great.
    thanks for sharing…..