fix doxygen generation
[obnox/wireshark/wip.git] / gtk / capture_if_dlg.c
1 /* capture_if_dlg.c
2  * Routines for the capture interface dialog
3  *
4  * $Id$
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 /* With MSVC and a libethereal.dll this file needs to import some variables 
26    in a special way. Therefore _NEED_VAR_IMPORT_ is defined. */
27 /*#define _NEED_VAR_IMPORT_*/
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #ifdef HAVE_LIBPCAP
34
35 #ifdef HAVE_SYS_STAT_H
36 # include <sys/stat.h>
37 #endif
38
39 #ifdef HAVE_SYS_WAIT_H
40 # include <sys/wait.h>
41 #endif
42
43
44 #include <pcap.h>
45
46 #include <gtk/gtk.h>
47
48 #include "globals.h"
49 #include "pcap-util.h"
50
51 #ifdef _WIN32
52 #include "capture-wpcap.h"
53 #endif
54
55 #include "compat_macros.h"
56 #include "simple_dialog.h"
57 #include "capture_dlg.h"
58
59 #include "ui_util.h"
60 #include "dlg_utils.h"
61
62 #include "wtap.h"
63 #include "capture.h"
64
65
66 extern gboolean is_capture_in_progress(void);
67
68 /*
69  * Keep a static pointer to the current "Capture Interfaces" window, if
70  * any, so that if somebody tries to do "Capture:Start" while there's
71  * already a "Capture Interfaces" window up, we just pop up the existing
72  * one, rather than creating a new one.
73  */
74 static GtkWidget *cap_if_w;
75
76 GList           *if_data = NULL;
77
78 guint           timer_id;
79
80 GtkWidget       *stop_bt;
81
82 GList           *if_list;
83
84 /*
85  * Timeout, in milliseconds, for reads from the stream of captured packets.
86  */
87 #define CAP_READ_TIMEOUT        250
88
89
90 /* the "runtime" data of one interface */
91 typedef struct if_dlg_data_s {
92     pcap_t      *pch;
93     GtkWidget   *device_lb;
94     GtkWidget   *descr_lb;
95     GtkWidget   *ip_lb;
96     GtkWidget   *curr_lb;
97     GtkWidget   *last_lb;
98     GtkWidget   *capture_bt;
99     GtkWidget   *prepare_bt;
100     guint32     last_packets;
101     gchar       *device;
102 } if_dlg_data_t;
103
104 void update_if(if_dlg_data_t *if_dlg_data);
105
106
107 /* start capture button was pressed */
108 static void
109 capture_do_cb(GtkWidget *capture_bt _U_, gpointer if_data)
110 {
111   if_dlg_data_t *if_dlg_data = if_data;
112
113   if (cfile.iface)
114     g_free(cfile.iface);
115
116   cfile.iface = g_strdup(if_dlg_data->device);
117
118   do_capture(NULL /* save_file */);
119 }
120
121
122 /* prepare capture button was pressed */
123 static void
124 capture_prepare_cb(GtkWidget *prepare_bt _U_, gpointer if_data)
125 {
126   if_dlg_data_t *if_dlg_data = if_data;
127
128   if (cfile.iface)
129     g_free(cfile.iface);
130
131   cfile.iface = g_strdup(if_dlg_data->device);
132
133   capture_prep_cb(NULL, NULL);
134 }
135
136
137 /* open a single interface */
138 void
139 open_if(gchar *name, if_dlg_data_t *if_dlg_data)
140 {
141   gchar       open_err_str[PCAP_ERRBUF_SIZE];
142
143   /*
144    * XXX - on systems with BPF, the number of BPF devices limits the
145    * number of devices on which you can capture simultaneously.
146    *
147    * This means that
148    *
149    *    1) this might fail if you run out of BPF devices
150    *
151    * and
152    *
153    *    2) opening every interface could leave too few BPF devices
154    *       for *other* programs.
155    *
156    * It also means the system could end up getting a lot of traffic
157    * that it has to pass through the networking stack and capture
158    * mechanism, so opening all the devices and presenting packet
159    * counts might not always be a good idea.
160    */
161   if_dlg_data->pch = pcap_open_live(name,
162                        MIN_PACKET_SIZE,
163                        capture_opts.promisc_mode, CAP_READ_TIMEOUT,
164                        open_err_str);
165
166   if (if_dlg_data->pch != NULL) {
167     update_if(if_dlg_data);
168   } else {
169     printf("open_if: %s\n", open_err_str);
170     gtk_label_set_text(GTK_LABEL(if_dlg_data->curr_lb), "error");
171     gtk_label_set_text(GTK_LABEL(if_dlg_data->last_lb), "error");
172   }
173 }
174
175 /* update a single interface */
176 void
177 update_if(if_dlg_data_t *if_dlg_data)
178 {
179   struct pcap_stat stats;
180   gchar *str;
181   guint diff;
182
183
184   /* pcap_stats() stats values differ on libpcap and winpcap!
185    * libpcap: returns the number of packets since pcap_open_live
186    * winpcap: returns the number of packets since the last pcap_stats call
187    * XXX - if that's true, that's a bug, and should be fixed; "pcap_stats()"
188    * is supposed to work the same way on all platforms, including Windows.
189    * Note that the WinPcap 3.0 documentation says "The values represent
190    * packet statistics from the start of the run to the time of the call."
191    * (Note also that some versions of libpcap, on some versions of UN*X,
192    * have the same bug.)
193    */
194   if (if_dlg_data->pch) {
195     if(pcap_stats(if_dlg_data->pch, &stats) >= 0) {
196 #ifdef _WIN32
197       diff = stats.ps_recv - if_dlg_data->last_packets;
198       if_dlg_data->last_packets = stats.ps_recv;
199 #else
200       diff = stats.ps_recv;
201       if_dlg_data->last_packets = stats.ps_recv + if_dlg_data->last_packets;
202 #endif    
203
204       str = g_strdup_printf("%u", if_dlg_data->last_packets);
205       gtk_label_set_text(GTK_LABEL(if_dlg_data->curr_lb), str);
206       g_free(str);
207       str = g_strdup_printf("%u", diff);
208       gtk_label_set_text(GTK_LABEL(if_dlg_data->last_lb), str);
209       g_free(str);
210
211       gtk_widget_set_sensitive(if_dlg_data->curr_lb, diff);
212       gtk_widget_set_sensitive(if_dlg_data->last_lb, diff);
213     } else {
214       gtk_label_set_text(GTK_LABEL(if_dlg_data->curr_lb), "error");
215       gtk_label_set_text(GTK_LABEL(if_dlg_data->last_lb), "error");
216     }
217   }
218 }
219
220
221 /* close a single interface */
222 void
223 close_if(if_dlg_data_t *if_dlg_data)
224 {
225     if(if_dlg_data->pch)
226         pcap_close(if_dlg_data->pch);
227 }
228
229
230
231 /* update all interfaces */
232 gboolean
233 update_all(gpointer data)
234 {
235     GList *curr;
236     int ifs;
237
238
239     if(!cap_if_w) {
240         return FALSE;
241     }
242
243     for(ifs = 0; (curr = g_list_nth(data, ifs)); ifs++) {
244         update_if(curr->data);
245     }
246
247     return TRUE;
248 }
249
250
251 /* a live capture has started or stopped */
252 void
253 set_capture_if_dialog_for_capture_in_progress(gboolean capture_in_progress)
254 {
255     GList *curr;
256     int ifs;
257
258     if(cap_if_w) {
259         gtk_widget_set_sensitive(stop_bt, capture_in_progress);
260         
261         for(ifs = 0; (curr = g_list_nth(if_data, ifs)); ifs++) {
262             if_dlg_data_t *if_dlg_data = curr->data;
263
264             gtk_widget_set_sensitive(if_dlg_data->capture_bt, !capture_in_progress);
265             gtk_widget_set_sensitive(if_dlg_data->prepare_bt, !capture_in_progress);
266         }        
267     }
268 }
269
270
271 /* the window was closed, cleanup things */
272 static void
273 capture_if_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
274 {
275     GList *curr;
276     int ifs;
277
278     gtk_timeout_remove(timer_id);
279
280     for(ifs = 0; (curr = g_list_nth(if_data, ifs)); ifs++) {
281         if_dlg_data_t *if_dlg_data = curr->data;
282
283         close_if(if_dlg_data);
284         g_free(curr->data);
285     }
286
287     if_data = NULL;
288
289     free_interface_list(if_list);
290
291     /* Note that we no longer have a "Capture Options" dialog box. */
292     cap_if_w = NULL;
293 }
294
295
296 /* start getting capture stats from all interfaces */
297 void
298 capture_if_cb(GtkWidget *w _U_, gpointer d _U_)
299 {
300   GtkWidget     *main_vb, *bbox, *close_bt;
301
302   GtkWidget     *if_tb;
303   GtkWidget     *if_lb;
304 #if GTK_MAJOR_VERSION < 2
305   GtkAccelGroup *accel_group;
306 #endif
307   GtkTooltips   *tooltips;
308   int           err;
309   char          err_str[PCAP_ERRBUF_SIZE];
310   gchar         *cant_get_if_list_errstr;
311   int           row;
312   if_dlg_data_t *if_dlg_data;
313   int           ifs;
314   GList         *curr;
315   if_info_t     *if_info;
316   GSList        *curr_ip;
317   if_addr_t     *ip_addr;
318   GString       *if_tool_str = g_string_new("");
319   gchar         *tmp_str;
320
321   if (cap_if_w != NULL) {
322     /* There's already a "Capture Interfaces" dialog box; reactivate it. */
323     reactivate_window(cap_if_w);
324     return;
325   }
326
327 #ifdef _WIN32
328   /* Is WPcap loaded? */
329   if (!has_wpcap) {
330           simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
331                   "Unable to load WinPcap (wpcap.dll); Ethereal will not be able\n"
332                   "to capture packets.\n\n"
333                   "In order to capture packets, WinPcap must be installed; see\n"
334                   "\n"
335                   "        http://winpcap.polito.it/\n"
336                   "\n"
337                   "or the mirror at\n"
338                   "\n"
339                   "        http://winpcap.mirror.ethereal.com/\n"
340                   "\n"
341                   "or the mirror at\n"
342                   "\n"
343                   "        http://www.mirrors.wiretapped.net/security/packet-capture/winpcap/\n"
344                   "\n"
345                   "for a downloadable version of WinPcap and for instructions\n"
346                   "on how to install WinPcap.");
347           return;
348   }
349 #endif
350
351   if_list = get_interface_list(&err, err_str);
352   if (if_list == NULL && err == CANT_GET_INTERFACE_LIST) {
353     cant_get_if_list_errstr = cant_get_if_list_error_message(err_str);
354     simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s",
355                   cant_get_if_list_errstr);
356     g_free(cant_get_if_list_errstr);
357     return;
358   }
359
360   cap_if_w = window_new(GTK_WINDOW_TOPLEVEL, "Ethereal: Capture Interfaces");
361
362   tooltips = gtk_tooltips_new();
363
364 #if GTK_MAJOR_VERSION < 2
365   /* Accelerator group for the accelerators (or, as they're called in
366      Windows and, I think, in Motif, "mnemonics"; Alt+<key> is a mnemonic,
367      Ctrl+<key> is an accelerator). */
368   accel_group = gtk_accel_group_new();
369   gtk_window_add_accel_group(GTK_WINDOW(cap_if_w), accel_group);
370 #endif
371
372   main_vb = gtk_vbox_new(FALSE, 0);
373   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
374   gtk_container_add(GTK_CONTAINER(cap_if_w), main_vb);
375
376
377   if_tb = gtk_table_new(6,1, FALSE);
378   gtk_table_set_row_spacings(GTK_TABLE(if_tb), 3);
379   gtk_table_set_col_spacings(GTK_TABLE(if_tb), 3);
380   gtk_container_add(GTK_CONTAINER(main_vb), if_tb);
381
382   row = 0;
383
384 #ifndef _WIN32
385   /*
386    * On Windows, device names are generally not meaningful - NT 5
387    * uses long blobs with GUIDs in them, for example - so we don't
388    * bother showing them.
389    */
390   if_lb = gtk_label_new("Device");
391   gtk_table_attach_defaults(GTK_TABLE(if_tb), if_lb, 0, 1, row, row+1);
392 #endif
393
394   if_lb = gtk_label_new("Description");
395   gtk_table_attach_defaults(GTK_TABLE(if_tb), if_lb, 1, 2, row, row+1);
396
397   if_lb = gtk_label_new(" IP ");
398   gtk_table_attach_defaults(GTK_TABLE(if_tb), if_lb, 2, 3, row, row+1);
399
400   if_lb = gtk_label_new("Packets");
401   gtk_table_attach_defaults(GTK_TABLE(if_tb), if_lb, 3, 4, row, row+1);
402
403   if_lb = gtk_label_new(" Packets/s ");
404   gtk_table_attach_defaults(GTK_TABLE(if_tb), if_lb, 4, 5, row, row+1);
405
406   stop_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_STOP);
407   gtk_tooltips_set_tip(tooltips, stop_bt, 
408           "Stop a running capture.", NULL);
409   gtk_table_attach_defaults(GTK_TABLE(if_tb), stop_bt, 5, 7, row, row+1);
410   SIGNAL_CONNECT(stop_bt, "clicked", capture_stop_cb, NULL);
411
412   row++;
413
414   for(ifs = 0; (curr = g_list_nth(if_list, ifs)); ifs++) {
415       g_string_assign(if_tool_str, "");
416       if_info = curr->data;
417       if_dlg_data = g_malloc0(sizeof(if_dlg_data_t));
418
419       /* device name */
420       if_dlg_data->device_lb = gtk_label_new(if_info->name);
421       if_dlg_data->device = if_info->name;
422 #ifndef _WIN32
423       gtk_misc_set_alignment(GTK_MISC(if_dlg_data->device_lb), 0.0, 0.5);
424       gtk_table_attach_defaults(GTK_TABLE(if_tb), if_dlg_data->device_lb, 0, 1, row, row+1);
425 #endif
426       g_string_append(if_tool_str, "Device: ");
427       g_string_append(if_tool_str, if_info->name);
428       g_string_append(if_tool_str, "\n");
429
430       /* description */
431       if (if_info->description != NULL)
432         if_dlg_data->descr_lb = gtk_label_new(if_info->description);
433       else
434         if_dlg_data->descr_lb = gtk_label_new("");
435       gtk_misc_set_alignment(GTK_MISC(if_dlg_data->descr_lb), 0.0, 0.5);
436       gtk_table_attach_defaults(GTK_TABLE(if_tb), if_dlg_data->descr_lb, 1, 2, row, row+1);
437
438       if (if_info->description) {
439         g_string_append(if_tool_str, "Description: ");
440         g_string_append(if_tool_str, if_info->description);
441         g_string_append(if_tool_str, "\n");
442       }
443
444       /* IP address */
445       /* only the first IP address will be shown */
446       g_string_append(if_tool_str, "IP: ");
447       curr_ip = g_slist_nth(if_info->ip_addr, 0);
448       if(curr_ip) {
449         ip_addr = (if_addr_t *)curr_ip->data;
450         switch (ip_addr->type) {
451
452         case AT_IPv4:
453           tmp_str = ip_to_str((guint8 *)&ip_addr->ip_addr.ip4_addr);
454           break;
455
456         case AT_IPv6:
457           tmp_str = ip6_to_str((struct e_in6_addr *)&ip_addr->ip_addr.ip6_addr);
458           break;
459
460         default:
461           g_assert_not_reached();
462           tmp_str = NULL;
463         }
464         if_dlg_data->ip_lb = gtk_label_new(tmp_str);
465         gtk_widget_set_sensitive(if_dlg_data->ip_lb, TRUE);
466         g_string_append(if_tool_str, tmp_str);
467       } else {
468         if_dlg_data->ip_lb = gtk_label_new("unknown");
469         gtk_widget_set_sensitive(if_dlg_data->ip_lb, FALSE);
470         g_string_append(if_tool_str, "unknown");
471       }
472       gtk_table_attach_defaults(GTK_TABLE(if_tb), if_dlg_data->ip_lb, 2, 3, row, row+1);
473       g_string_append(if_tool_str, "\n");
474
475       /* packets */
476       if_dlg_data->curr_lb = gtk_label_new("-");
477       gtk_table_attach_defaults(GTK_TABLE(if_tb), if_dlg_data->curr_lb, 3, 4, row, row+1);
478
479       /* packets/s */
480       if_dlg_data->last_lb = gtk_label_new("-");
481       gtk_table_attach_defaults(GTK_TABLE(if_tb), if_dlg_data->last_lb, 4, 5, row, row+1);
482
483       /* capture button */
484       if_dlg_data->capture_bt = gtk_button_new_with_label("Capture");
485       SIGNAL_CONNECT(if_dlg_data->capture_bt, "clicked", capture_do_cb, if_dlg_data);
486       tmp_str = g_strdup_printf("Immediately start a capture from this interface:\n\n%s", if_tool_str->str);
487       gtk_tooltips_set_tip(tooltips, if_dlg_data->capture_bt, 
488           tmp_str, NULL);
489       g_free(tmp_str);
490       gtk_table_attach_defaults(GTK_TABLE(if_tb), if_dlg_data->capture_bt, 5, 6, row, row+1);
491
492       /* prepare button */
493       if_dlg_data->prepare_bt = gtk_button_new_with_label("Prepare");
494       SIGNAL_CONNECT(if_dlg_data->prepare_bt, "clicked", capture_prepare_cb, if_dlg_data);
495       gtk_tooltips_set_tip(tooltips, if_dlg_data->prepare_bt, 
496           "Open the capture options dialog with this interface selected.", NULL);
497       gtk_table_attach_defaults(GTK_TABLE(if_tb), if_dlg_data->prepare_bt, 6, 7, row, row+1);
498
499       open_if(if_info->name, if_dlg_data);
500
501       if_data = g_list_append(if_data, if_dlg_data);
502
503       row++;
504   }
505
506   g_string_free(if_tool_str, TRUE);
507
508   /* Button row: close button */
509   bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
510   gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 5);
511
512   close_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_CLOSE);
513   window_set_cancel_button(cap_if_w, close_bt, window_cancel_button_cb);
514   gtk_tooltips_set_tip(tooltips, close_bt, "Close this window.", NULL);
515
516   gtk_widget_grab_default(close_bt);
517
518   SIGNAL_CONNECT(cap_if_w, "delete_event", window_delete_event_cb, NULL);
519   SIGNAL_CONNECT(cap_if_w, "destroy", capture_if_destroy_cb, NULL);
520
521   gtk_widget_show_all(cap_if_w);
522   window_present(cap_if_w);
523
524   set_capture_if_dialog_for_capture_in_progress(is_capture_in_progress());
525
526     /* update the interface list every 1000ms */
527   timer_id = gtk_timeout_add(1000, update_all, if_data);
528 }
529
530
531 #endif /* HAVE_LIBPCAP */