Add support for IPv6 addresses for interfaces.
[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 #if 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   if_lb = gtk_label_new("Device");
386   gtk_table_attach_defaults(GTK_TABLE(if_tb), if_lb, 0, 1, row, row+1);
387 #endif
388
389   if_lb = gtk_label_new("Description");
390   gtk_table_attach_defaults(GTK_TABLE(if_tb), if_lb, 1, 2, row, row+1);
391
392   if_lb = gtk_label_new(" IP ");
393   gtk_table_attach_defaults(GTK_TABLE(if_tb), if_lb, 2, 3, row, row+1);
394
395   if_lb = gtk_label_new("Packets");
396   gtk_table_attach_defaults(GTK_TABLE(if_tb), if_lb, 3, 4, row, row+1);
397
398   if_lb = gtk_label_new(" Packets/s ");
399   gtk_table_attach_defaults(GTK_TABLE(if_tb), if_lb, 4, 5, row, row+1);
400
401   stop_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_STOP);
402   gtk_tooltips_set_tip(tooltips, stop_bt, 
403           "Stop a running capture.", NULL);
404   gtk_table_attach_defaults(GTK_TABLE(if_tb), stop_bt, 5, 7, row, row+1);
405   SIGNAL_CONNECT(stop_bt, "clicked", capture_stop_cb, NULL);
406
407   row++;
408
409   for(ifs = 0; (curr = g_list_nth(if_list, ifs)); ifs++) {
410       g_string_assign(if_tool_str, "");
411       if_info = curr->data;
412       if_dlg_data = g_malloc0(sizeof(if_dlg_data_t));
413
414       /* device name */
415       if_dlg_data->device_lb = gtk_label_new(if_info->name);
416       if_dlg_data->device = if_info->name;
417 #ifndef WIN32
418       gtk_misc_set_alignment(GTK_MISC(if_dlg_data->device_lb), 0.0, 0.5);
419       gtk_table_attach_defaults(GTK_TABLE(if_tb), if_dlg_data->device_lb, 0, 1, row, row+1);
420 #endif
421       g_string_append(if_tool_str, "Device: ");
422       g_string_append(if_tool_str, if_info->name);
423       g_string_append(if_tool_str, "\n");
424
425       /* description */
426       if (if_info->description != NULL)
427         if_dlg_data->descr_lb = gtk_label_new(if_info->description);
428       else
429         if_dlg_data->descr_lb = gtk_label_new("");
430       gtk_misc_set_alignment(GTK_MISC(if_dlg_data->descr_lb), 0.0, 0.5);
431       gtk_table_attach_defaults(GTK_TABLE(if_tb), if_dlg_data->descr_lb, 1, 2, row, row+1);
432
433       if (if_info->description) {
434         g_string_append(if_tool_str, "Description: ");
435         g_string_append(if_tool_str, if_info->description);
436         g_string_append(if_tool_str, "\n");
437       }
438
439       /* IP address */
440       /* only the first IP address will be shown */
441       g_string_append(if_tool_str, "IP: ");
442       curr_ip = g_slist_nth(if_info->ip_addr, 0);
443       if(curr_ip) {
444         ip_addr = (if_addr_t *)curr_ip->data;
445         switch (ip_addr->family) {
446
447         case FAM_IPv4:
448           tmp_str = ip_to_str((guint8 *)&ip_addr->ip_addr.ip4_addr);
449           break;
450
451         case FAM_IPv6:
452           tmp_str = ip6_to_str((struct e_in6_addr *)&ip_addr->ip_addr.ip6_addr);
453           break;
454
455         default:
456           g_assert_not_reached();
457           tmp_str = NULL;
458         }
459         if_dlg_data->ip_lb = gtk_label_new(tmp_str);
460         gtk_widget_set_sensitive(if_dlg_data->ip_lb, TRUE);
461         g_string_append(if_tool_str, tmp_str);
462       } else {
463         if_dlg_data->ip_lb = gtk_label_new("unknown");
464         gtk_widget_set_sensitive(if_dlg_data->ip_lb, FALSE);
465         g_string_append(if_tool_str, "unknown");
466       }
467       gtk_table_attach_defaults(GTK_TABLE(if_tb), if_dlg_data->ip_lb, 2, 3, row, row+1);
468       g_string_append(if_tool_str, "\n");
469
470       /* packets */
471       if_dlg_data->curr_lb = gtk_label_new("-");
472       gtk_table_attach_defaults(GTK_TABLE(if_tb), if_dlg_data->curr_lb, 3, 4, row, row+1);
473
474       /* packets/s */
475       if_dlg_data->last_lb = gtk_label_new("-");
476       gtk_table_attach_defaults(GTK_TABLE(if_tb), if_dlg_data->last_lb, 4, 5, row, row+1);
477
478       /* capture button */
479       if_dlg_data->capture_bt = gtk_button_new_with_label("Capture");
480       SIGNAL_CONNECT(if_dlg_data->capture_bt, "clicked", capture_do_cb, if_dlg_data);
481       tmp_str = g_strdup_printf("Immediately start a capture from this interface:\n\n%s", if_tool_str->str);
482       gtk_tooltips_set_tip(tooltips, if_dlg_data->capture_bt, 
483           tmp_str, NULL);
484       g_free(tmp_str);
485       gtk_table_attach_defaults(GTK_TABLE(if_tb), if_dlg_data->capture_bt, 5, 6, row, row+1);
486
487       /* prepare button */
488       if_dlg_data->prepare_bt = gtk_button_new_with_label("Prepare");
489       SIGNAL_CONNECT(if_dlg_data->prepare_bt, "clicked", capture_prepare_cb, if_dlg_data);
490       gtk_tooltips_set_tip(tooltips, if_dlg_data->prepare_bt, 
491           "Open the capture options dialog with this interface selected.", NULL);
492       gtk_table_attach_defaults(GTK_TABLE(if_tb), if_dlg_data->prepare_bt, 6, 7, row, row+1);
493
494       open_if(if_info->name, if_dlg_data);
495
496       if_data = g_list_append(if_data, if_dlg_data);
497
498       row++;
499   }
500
501   g_string_free(if_tool_str, TRUE);
502
503   /* Button row: close button */
504   bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
505   gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 5);
506
507   close_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_CLOSE);
508   window_set_cancel_button(cap_if_w, close_bt, window_cancel_button_cb);
509   gtk_tooltips_set_tip(tooltips, close_bt, "Close this window.", NULL);
510
511   gtk_widget_grab_default(close_bt);
512
513   SIGNAL_CONNECT(cap_if_w, "delete_event", window_delete_event_cb, NULL);
514   SIGNAL_CONNECT(cap_if_w, "destroy", capture_if_destroy_cb, NULL);
515
516   gtk_widget_show_all(cap_if_w);
517   window_present(cap_if_w);
518
519   set_capture_if_dialog_for_capture_in_progress(is_capture_in_progress());
520
521     /* update the interface list every 1000ms */
522   timer_id = gtk_timeout_add(1000, update_all, if_data);
523 }
524
525
526 #endif /* HAVE_LIBPCAP */