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