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