Initial revision
[obnox/wireshark/wip.git] / capture.c
1 /* capture.c
2  * Routines for packet capture windows
3  *
4  * Ethereal - Network traffic analyzer
5  * By Gerald Combs <gerald@zing.org>
6  * Copyright 1998 Gerald Combs
7  *
8  * 
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  * 
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  * 
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <gtk/gtk.h>
29 #include <pcap.h>
30
31 #include <string.h>
32 #include <sys/socket.h>
33 #include <sys/ioctl.h>
34 #include <net/if.h>
35
36 #ifdef HAVE_SYS_SOCKIO_H
37 # include <sys/sockio.h>
38 #endif
39
40 #include "packet.h"
41 #include "file.h"
42 #include "capture.h"
43 #include "etypes.h"
44 #include "util.h"
45
46 extern capture_file  cf;
47 extern GtkWidget    *info_bar;
48 extern guint         file_ctx;
49
50 /* File selection data keys */
51 const gchar *prep_fs_key = "prep_fs",
52             *prep_te_key = "prep_te";
53
54 /* Capture callback data keys */
55 const gchar *cap_iface_key = "cap_iface",
56             *cap_file_key  = "cap_file",
57             *cap_count_key = "cap_count",
58             *cap_open_key  = "cap_open",
59             *cap_snap_key  = "cap_snap";
60
61 GList *
62 get_interface_list() {
63   GList  *il = NULL;
64   struct  ifreq *ifr, *last;
65   struct  ifconf ifc;
66   int     sock = socket(AF_INET, SOCK_DGRAM, 0);
67
68   if (sock < 0)
69   {
70     simple_dialog(ESD_TYPE_WARN, NULL,
71       "Can't list interfaces: error opening socket.");
72     return NULL;
73   }
74
75   /* Since we have to grab the interface list all at once, we'll make
76      plenty of room */
77   ifc.ifc_len = 1024 * sizeof(struct ifreq);
78   ifc.ifc_buf = malloc(ifc.ifc_len);
79
80   if (ioctl(sock, SIOCGIFCONF, &ifc) < 0 ||
81     ifc.ifc_len < sizeof(struct ifreq))
82   {
83     simple_dialog(ESD_TYPE_WARN, NULL,
84       "Can't list interfaces: ioctl error.");
85     return NULL;
86   }
87
88   ifr  = (struct ifreq *) ifc.ifc_req;
89   last = (struct ifreq *) ((char *) ifr + ifc.ifc_len);
90   while (ifr < last)
91   {
92     /*
93      * What we want:
94      * - Interfaces that are up, and not loopback
95      * - IP interfaces (do we really need this?)
96      * - Anything that doesn't begin with "lo" (loopback again) or "dummy"
97      * - Anything that doesn't include a ":" (Solaris virtuals)
98      */
99     if (! (ifr->ifr_flags & (IFF_UP | IFF_LOOPBACK)) &&
100         (ifr->ifr_addr.sa_family == AF_INET) &&
101         strncmp(ifr->ifr_name, "lo", 2) &&
102         strncmp(ifr->ifr_name, "dummy", 5) &&
103         ! strchr(ifr->ifr_name, ':')) {
104       il = g_list_append(il, g_strdup(ifr->ifr_name));
105     }
106 #ifdef HAVE_SOCKADDR_SA_LEN
107     ifr = (struct ifreq *) ((char *) ifr + ifr->ifr_addr.sa_len + IFNAMSIZ);
108 #else
109     ifr = (struct ifreq *) ((char *) ifr + sizeof(struct ifreq));
110 #endif
111   }
112
113   free(ifc.ifc_buf);
114   return il;
115 }
116
117 void
118 capture_prep_cb(GtkWidget *w, gpointer d) {
119   GtkWidget     *cap_open_w, *if_cb, *if_lb, *file_te, *file_bt,
120                 *count_lb, *count_cb, *main_vb, *top_hb, *middle_hb,
121                 *bottom_hb, *bbox, *ok_bt, *cancel_bt, *capfile_ck,
122                 *snap_lb, *snap_sb;
123   GtkAdjustment *adj;
124   GList         *if_list, *count_list = NULL;
125   gchar         *count_item1 = "0 (Infinite)", count_item2[16];
126
127   cap_open_w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
128   gtk_window_set_title(GTK_WINDOW(cap_open_w), "Ethereal: Capture Preferences");
129   
130   /* Container for each row of widgets */
131   main_vb = gtk_vbox_new(FALSE, 3);
132   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
133   gtk_container_add(GTK_CONTAINER(cap_open_w), main_vb);
134   gtk_widget_show(main_vb);
135   
136   /* Top row: Interface and count selections */
137   top_hb = gtk_hbox_new(FALSE, 1);
138   gtk_container_add(GTK_CONTAINER(main_vb), top_hb);
139   gtk_widget_show(top_hb);
140   
141   if_lb = gtk_label_new("Interface:");
142   gtk_box_pack_start(GTK_BOX(top_hb), if_lb, FALSE, FALSE, 3);
143   gtk_widget_show(if_lb);
144   
145   if_list = get_interface_list();
146   if_cb = gtk_combo_new();
147   gtk_combo_set_popdown_strings(GTK_COMBO(if_cb), if_list);
148   if (cf.iface)
149     gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry), cf.iface);
150   else if (if_list)
151     gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry), if_list->data);
152   gtk_box_pack_start(GTK_BOX(top_hb), if_cb, FALSE, FALSE, 3);
153   gtk_widget_show(if_cb);
154   while (if_list) {
155     g_free(if_list->data);
156     if_list = g_list_remove_link(if_list, if_list);
157   }
158
159   if (cf.count) {
160     snprintf(count_item2, 15, "%d", cf.count);
161     count_list = g_list_append(count_list, count_item2);
162   }
163   count_list = g_list_append(count_list, count_item1);
164   count_cb = gtk_combo_new();
165   gtk_combo_set_popdown_strings(GTK_COMBO(count_cb), count_list);
166   gtk_box_pack_end(GTK_BOX(top_hb), count_cb, FALSE, FALSE, 3);
167   gtk_widget_show(count_cb);
168   while (count_list)
169     count_list = g_list_remove_link(count_list, count_list);
170
171   count_lb = gtk_label_new("Count:");
172   gtk_box_pack_end(GTK_BOX(top_hb), count_lb, FALSE, FALSE, 3);
173   gtk_widget_show(count_lb);
174   
175   /* Middle row: File: button and text entry */
176   middle_hb = gtk_hbox_new(FALSE, 1);
177   gtk_container_add(GTK_CONTAINER(main_vb), middle_hb);
178   gtk_widget_show(middle_hb);
179   
180   file_bt = gtk_button_new_with_label("File:");
181   gtk_box_pack_start(GTK_BOX(middle_hb), file_bt, FALSE, FALSE, 3);
182   gtk_widget_show(file_bt);
183   
184   file_te = gtk_entry_new();
185   if (cf.save_file)
186     gtk_entry_set_text(GTK_ENTRY(file_te), cf.save_file);
187   gtk_box_pack_start(GTK_BOX(middle_hb), file_te, TRUE, TRUE, 3);
188   gtk_widget_show(file_te);
189
190   gtk_signal_connect_object(GTK_OBJECT(file_bt), "clicked",
191     GTK_SIGNAL_FUNC(capture_prep_file_cb), GTK_OBJECT(file_te));
192
193   /* Bottom row: Capture file checkbox and snap spinbutton */
194   bottom_hb = gtk_hbox_new(FALSE, 1);
195   gtk_container_add(GTK_CONTAINER(main_vb), bottom_hb);
196   gtk_widget_show(bottom_hb);
197   
198   capfile_ck = gtk_check_button_new_with_label("Open file after capture");
199   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(capfile_ck), TRUE);
200   gtk_box_pack_start(GTK_BOX(bottom_hb), capfile_ck, FALSE, FALSE, 3);
201   gtk_widget_show(capfile_ck);
202   
203   snap_lb = gtk_label_new("Capture length");
204   gtk_misc_set_alignment(GTK_MISC(snap_lb), 0, 0.5);
205   gtk_box_pack_start(GTK_BOX(bottom_hb), snap_lb, FALSE, FALSE, 6);
206   gtk_widget_show(snap_lb);
207
208   adj = (GtkAdjustment *) gtk_adjustment_new((float) cf.snap, 1.0, 4096.0,
209     1.0, 10.0, 0.0);
210   snap_sb = gtk_spin_button_new (adj, 0, 0);
211   gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (snap_sb), TRUE);
212   gtk_widget_set_usize (snap_sb, 80, 0);
213   gtk_box_pack_start (GTK_BOX(bottom_hb), snap_sb, FALSE, FALSE, 3); 
214   gtk_widget_show(snap_sb);
215   
216   /* Button row: OK and cancel buttons */
217   bbox = gtk_hbutton_box_new();
218   gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
219   gtk_container_add(GTK_CONTAINER(main_vb), bbox);
220   gtk_widget_show(bbox);
221   
222   ok_bt = gtk_button_new_with_label ("OK");
223   gtk_signal_connect_object(GTK_OBJECT(ok_bt), "clicked",
224     GTK_SIGNAL_FUNC(capture_prep_ok_cb), GTK_OBJECT(cap_open_w));
225   gtk_container_add(GTK_CONTAINER(bbox), ok_bt);
226 /*  GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
227   gtk_widget_grab_default(ok_bt);  */
228   gtk_widget_show(ok_bt);
229
230   cancel_bt = gtk_button_new_with_label ("Cancel");
231   gtk_signal_connect_object(GTK_OBJECT(cancel_bt), "clicked",
232     GTK_SIGNAL_FUNC(capture_prep_close_cb), GTK_OBJECT(cap_open_w));
233   gtk_container_add(GTK_CONTAINER(bbox), cancel_bt);
234   gtk_widget_show(cancel_bt);
235
236   /* Attach pointers to needed widges to the capture prefs window/object */
237   gtk_object_set_data(GTK_OBJECT(cap_open_w), cap_iface_key, if_cb);
238   gtk_object_set_data(GTK_OBJECT(cap_open_w), cap_file_key,  file_te);
239   gtk_object_set_data(GTK_OBJECT(cap_open_w), cap_count_key, count_cb);
240   gtk_object_set_data(GTK_OBJECT(cap_open_w), cap_open_key,  capfile_ck);
241   gtk_object_set_data(GTK_OBJECT(cap_open_w), cap_snap_key,  snap_sb);
242   
243   gtk_widget_show(cap_open_w);
244 }
245
246 void
247 capture_prep_file_cb(GtkWidget *w, gpointer te) {
248   GtkWidget *fs;
249
250   fs = gtk_file_selection_new ("Ethereal: Open Save File");
251
252   gtk_object_set_data(GTK_OBJECT(w), prep_fs_key, fs);
253   gtk_object_set_data(GTK_OBJECT(w), prep_te_key, (GtkWidget *) te);
254   
255   gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION(fs)->ok_button),
256     "clicked", (GtkSignalFunc) cap_prep_fs_ok_cb, w);
257
258   /* Connect the cancel_button to destroy the widget */
259   gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION(fs)->cancel_button),
260     "clicked", (GtkSignalFunc) cap_prep_fs_cancel_cb, w);
261   
262   gtk_widget_show(fs);
263 }
264
265 void
266 cap_prep_fs_ok_cb(GtkWidget *w, gpointer data) {
267   GtkWidget *fs, *te;
268   
269   fs = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(data), prep_fs_key);
270   te = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(data), prep_te_key);
271
272   gtk_entry_set_text(GTK_ENTRY(te),
273     gtk_file_selection_get_filename (GTK_FILE_SELECTION(fs)));
274   cap_prep_fs_cancel_cb(w, data);
275 }
276
277 void
278 cap_prep_fs_cancel_cb(GtkWidget *w, gpointer data) {
279   GtkWidget *fs;
280   
281   fs = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(data), prep_fs_key);
282
283   gtk_widget_destroy(fs);
284 }  
285
286 void
287 capture_prep_ok_cb(GtkWidget *w, gpointer data) {
288   GtkWidget *if_cb, *file_te, *count_cb, *open_ck, *snap_sb;
289   gchar   *file;
290   gint     open;
291
292   if_cb    = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(data), cap_iface_key);
293   file_te  = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(data), cap_file_key);
294   count_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(data), cap_count_key);
295   open_ck  = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(data), cap_open_key);
296   snap_sb  = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(data), cap_snap_key);
297
298   if (cf.iface) g_free(cf.iface);
299   cf.iface =
300     g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry)));
301   if (cf.save_file) g_free(cf.save_file);
302   cf.save_file = g_strdup(gtk_entry_get_text(GTK_ENTRY(file_te)));
303   cf.count =
304     atoi(g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(count_cb)->entry))));
305   open = GTK_TOGGLE_BUTTON(open_ck)->active;
306   cf.snap = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(snap_sb));
307   if (cf.snap < 1)
308     cf.snap = 4096;
309   else if (cf.snap < 68)
310     cf.snap = 68;
311
312   gtk_widget_destroy(GTK_WIDGET(data));
313   
314   capture(open);
315 }
316
317 void
318 capture_prep_close_cb(GtkWidget *w, gpointer win) {
319
320   gtk_grab_remove(GTK_WIDGET(win));
321   gtk_widget_destroy(GTK_WIDGET(win));
322 }
323
324 void
325 capture(gint open) {
326   GtkWidget  *cap_w, *main_vb, *count_lb, *tcp_lb, *udp_lb, *other_lb,
327              *stop_bt;
328   pcap_t     *pch;
329   gchar       err_str[PCAP_ERRBUF_SIZE], label_str[32];
330   loop_data   ld;
331   bpf_u_int32 netnum, netmask;
332   time_t      upd_time, cur_time;
333   
334   ld.go    = TRUE;
335   ld.count = 0;
336   ld.max   = cf.count;
337   ld.tcp   = 0;
338   ld.udp   = 0;
339   ld.other = 0;
340   ld.pdh   = NULL;
341
342   close_cap_file(&cf, info_bar, file_ctx);
343
344   pch = pcap_open_live(cf.iface, cf.snap, 1, 250, err_str);
345
346   if (pch) {
347     if (cf.save_file[0]) {
348       ld.pdh = pcap_dump_open(pch, cf.save_file);
349       if (ld.pdh == NULL) {  /* We have an error */
350         snprintf(err_str, PCAP_ERRBUF_SIZE, "Error trying to open dump "
351           "file:\n%s", pcap_geterr(pch));
352         simple_dialog(ESD_TYPE_WARN, NULL, err_str);
353         g_free(cf.save_file);
354         cf.save_file = NULL;
355         pcap_close(pch);
356         return;
357       }
358     }
359
360     if (cf.filter) {
361       if (pcap_lookupnet (cf.iface, &netnum, &netmask, err_str) < 0) {
362         simple_dialog(ESD_TYPE_WARN, NULL,
363           "Can't use filter:  Couldn't obtain netmask info.");
364         return;
365       } else if (pcap_compile(pch, &cf.fcode, cf.filter, 1, netmask) < 0) {
366         simple_dialog(ESD_TYPE_WARN, NULL, "Unable to parse filter string.");
367         return;
368       } else if (pcap_setfilter(pch, &cf.fcode) < 0) {
369         simple_dialog(ESD_TYPE_WARN, NULL, "Can't install filter.");
370         return;
371       }
372     }
373
374     cap_w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
375     gtk_window_set_title(GTK_WINDOW(cap_w), "Ethereal: Capture / Playback");
376
377     /* Container for capture display widgets */
378     main_vb = gtk_vbox_new(FALSE, 1);
379     gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
380     gtk_container_add(GTK_CONTAINER(cap_w), main_vb);
381     gtk_widget_show(main_vb);
382
383     count_lb = gtk_label_new("Count: 0");
384     gtk_box_pack_start(GTK_BOX(main_vb), count_lb, FALSE, FALSE, 3);
385     gtk_widget_show(count_lb);
386
387     tcp_lb = gtk_label_new("TCP: 0 (0.0%)");
388     gtk_box_pack_start(GTK_BOX(main_vb), tcp_lb, FALSE, FALSE, 3);
389     gtk_widget_show(tcp_lb);
390
391     udp_lb = gtk_label_new("UDP: 0 (0.0%)");
392     gtk_box_pack_start(GTK_BOX(main_vb), udp_lb, FALSE, FALSE, 3);
393     gtk_widget_show(udp_lb);
394
395     other_lb = gtk_label_new("Other: 0 (0.0%)");
396     gtk_box_pack_start(GTK_BOX(main_vb), other_lb, FALSE, FALSE, 3);
397     gtk_widget_show(other_lb);
398
399     stop_bt = gtk_button_new_with_label ("Stop");
400     gtk_signal_connect(GTK_OBJECT(stop_bt), "clicked",
401       GTK_SIGNAL_FUNC(capture_stop_cb), (gpointer) &ld);
402     gtk_box_pack_end(GTK_BOX(main_vb), stop_bt, FALSE, FALSE, 3);
403     GTK_WIDGET_SET_FLAGS(stop_bt, GTK_CAN_DEFAULT);
404     gtk_widget_grab_default(stop_bt);
405     GTK_WIDGET_SET_FLAGS(stop_bt, GTK_CAN_DEFAULT);
406     gtk_widget_grab_default(stop_bt);
407     gtk_widget_show(stop_bt);
408
409     gtk_widget_show(cap_w);
410     gtk_grab_add(cap_w);
411
412     upd_time = time(NULL);
413     while (ld.go) {
414       while (gtk_events_pending()) gtk_main_iteration();
415       pcap_dispatch(pch, 1, capture_pcap_cb, (u_char *) &ld);
416       /* Only update once a second so as not to overload slow displays */
417       cur_time = time(NULL);
418       if (cur_time > upd_time) {
419         upd_time = cur_time;
420         sprintf(label_str, "Count: %d", ld.count);
421         gtk_label_set(GTK_LABEL(count_lb), label_str);
422         sprintf(label_str, "TCP: %d (%.1f%%)", ld.tcp, pct(ld.tcp, ld.count));
423         gtk_label_set(GTK_LABEL(tcp_lb), label_str);
424         sprintf(label_str, "UDP: %d (%.1f%%)", ld.udp, pct(ld.udp, ld.count));
425         gtk_label_set(GTK_LABEL(udp_lb), label_str);
426         sprintf(label_str, "Other: %d (%.1f%%)", ld.other,
427           pct(ld.other, ld.count));
428         gtk_label_set(GTK_LABEL(other_lb), label_str);
429       }
430     }
431     
432     if (ld.pdh) pcap_dump_close(ld.pdh);
433     pcap_close(pch);
434
435     gtk_grab_remove(GTK_WIDGET(cap_w));
436     gtk_widget_destroy(GTK_WIDGET(cap_w));
437   } else {
438     while (gtk_events_pending()) gtk_main_iteration();
439     simple_dialog(ESD_TYPE_WARN, NULL,
440       "The capture session could not be initiated.  Please\n"
441       "check to make sure you have sufficient permissions, and\n"
442       "that you have the proper interface specified.");
443     g_free(cf.save_file);
444     cf.save_file = NULL;
445   }
446
447   if (cf.save_file && open) load_cap_file(cf.save_file, &cf);
448 }
449
450 float
451 pct(gint num, gint denom) {
452   if (denom) {
453     return (float) num * 100.0 / (float) denom;
454   } else {
455     return 0.0;
456   }
457 }
458
459 void
460 capture_stop_cb(GtkWidget *w, gpointer data) {
461   loop_data *ld = (loop_data *) data;
462   
463   ld->go = FALSE;
464 }
465
466 void
467 capture_pcap_cb(u_char *user, const struct pcap_pkthdr *phdr,
468   const u_char *pd) {
469   
470   guint16 etype;
471   guint8  iptype = 0;
472   gint    offset = 14;
473   
474   loop_data *ld = (loop_data *) user;
475   
476   if ((++ld->count >= ld->max) && (ld->max > 0)) ld->go = FALSE;
477   /* Currently, pcap_dumper_t is a FILE *.  Let's hope that doesn't change. */
478   if (ld->pdh) pcap_dump((u_char *) ld->pdh, phdr, pd);
479   
480   etype = etype = (pd[12] << 8) | pd[13];
481   if (etype <= IEEE_802_3_MAX_LEN) {
482     etype = (pd[20] << 8) | pd[21];
483     offset = 22;
484   }
485   
486   if (etype == ETHERTYPE_IP) {
487     iptype = pd[offset + 9];
488     switch (iptype) {
489       case 6:
490         ld->tcp++;
491         break;
492       case 17:
493         ld->udp++;
494         break;
495       default:
496         ld->other++;
497     }
498   } else {
499     ld->other++;
500   }
501 }