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
Comments on this entry are closed.
Great article but…could you provide the complete t.cpp source code? π
Great write up. Thanks a bunch.
-Deniz
Great work. Thanks!!!
Thanks for sharing. It helped me.
Hi,
Thanks a lot for nice article
To stay up to date>
Since C99 it is possible to use restrict as well in situations like this>
const char *msg_format
this is a great share! Could please provide full source code so that we could play with it? Thank you in advance~
I was digging through tshark code for using the dissectors….this is great.
thanks for sharing…..