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