2 * Routines for packet capture windows
4 * $Id: capture.c,v 1.30 1999/07/09 04:18:32 gram Exp $
6 * Ethereal - Network traffic analyzer
7 * By Gerald Combs <gerald@zing.org>
8 * Copyright 1998 Gerald Combs
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.
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.
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.
33 #ifdef HAVE_SYS_TYPES_H
34 # include <sys/types.h>
43 #include <sys/socket.h>
44 #include <sys/ioctl.h>
49 #ifdef NEED_SNPRINTF_H
55 # include "snprintf.h"
58 #ifdef HAVE_SYS_SOCKIO_H
59 # include <sys/sockio.h>
71 extern capture_file cf;
72 extern GtkWidget *info_bar;
73 extern guint file_ctx;
75 extern gchar *ethereal_path;
76 extern gchar *medium_font;
77 extern gchar *bold_font;
79 extern int sync_pipe[];
81 extern int sigusr2_received;
82 extern int quit_after_cap;
84 /* Capture callback data keys */
85 #define E_CAP_IFACE_KEY "cap_iface"
86 #define E_CAP_FILT_KEY "cap_filter"
87 #define E_CAP_COUNT_KEY "cap_count"
88 #define E_CAP_OPEN_KEY "cap_open"
89 #define E_CAP_SNAP_KEY "cap_snap"
91 /* Capture filter key */
92 #define E_CAP_FILT_TE_KEY "cap_filt_te"
94 static void capture_prep_ok_cb(GtkWidget *, gpointer);
95 static void capture_prep_close_cb(GtkWidget *, gpointer);
96 static float pct(gint, gint);
97 static void capture_stop_cb(GtkWidget *, gpointer);
98 static void capture_pcap_cb(u_char *, const struct pcap_pkthdr *,
102 get_interface_list() {
104 struct ifreq *ifr, *last;
106 int sock = socket(AF_INET, SOCK_DGRAM, 0);
110 simple_dialog(ESD_TYPE_WARN, NULL,
111 "Can't list interfaces: error opening socket.");
115 /* Since we have to grab the interface list all at once, we'll make
117 ifc.ifc_len = 1024 * sizeof(struct ifreq);
118 ifc.ifc_buf = malloc(ifc.ifc_len);
120 if (ioctl(sock, SIOCGIFCONF, &ifc) < 0 ||
121 ifc.ifc_len < sizeof(struct ifreq))
123 simple_dialog(ESD_TYPE_WARN, NULL,
124 "Can't list interfaces: ioctl error.");
128 ifr = (struct ifreq *) ifc.ifc_req;
129 last = (struct ifreq *) ((char *) ifr + ifc.ifc_len);
134 * - Interfaces that are up, and not loopback
135 * - IP interfaces (do we really need this?)
136 * - Anything that doesn't begin with "lo" (loopback again) or "dummy"
137 * - Anything that doesn't include a ":" (Solaris virtuals)
139 if (! (ifr->ifr_flags & (IFF_UP | IFF_LOOPBACK)) &&
140 (ifr->ifr_addr.sa_family == AF_INET) &&
141 strncmp(ifr->ifr_name, "lo", 2) &&
142 strncmp(ifr->ifr_name, "dummy", 5) &&
143 ! strchr(ifr->ifr_name, ':')) {
144 il = g_list_append(il, g_strdup(ifr->ifr_name));
147 ifr = (struct ifreq *) ((char *) ifr + ifr->ifr_addr.sa_len + IFNAMSIZ);
149 ifr = (struct ifreq *) ((char *) ifr + sizeof(struct ifreq));
158 capture_prep_cb(GtkWidget *w, gpointer d) {
159 GtkWidget *cap_open_w, *if_cb, *if_lb,
160 *count_lb, *count_cb, *main_vb, *if_hb, *count_hb,
161 *filter_hb, *filter_bt, *filter_te, *caplen_hb,
162 *bbox, *ok_bt, *cancel_bt, *snap_lb,
165 GList *if_list, *count_list = NULL;
166 gchar *count_item1 = "0 (Infinite)", count_item2[16];
168 cap_open_w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
169 gtk_window_set_title(GTK_WINDOW(cap_open_w), "Ethereal: Capture Preferences");
171 /* Container for each row of widgets */
172 main_vb = gtk_vbox_new(FALSE, 3);
173 gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
174 gtk_container_add(GTK_CONTAINER(cap_open_w), main_vb);
175 gtk_widget_show(main_vb);
178 if_hb = gtk_hbox_new(FALSE, 3);
179 gtk_container_add(GTK_CONTAINER(main_vb), if_hb);
180 gtk_widget_show(if_hb);
182 if_lb = gtk_label_new("Interface:");
183 gtk_box_pack_start(GTK_BOX(if_hb), if_lb, FALSE, FALSE, 0);
184 gtk_widget_show(if_lb);
186 if_list = get_interface_list();
188 if_cb = gtk_combo_new();
189 gtk_combo_set_popdown_strings(GTK_COMBO(if_cb), if_list);
191 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry), cf.iface);
193 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry), if_list->data);
194 gtk_box_pack_start(GTK_BOX(if_hb), if_cb, FALSE, FALSE, 0);
195 gtk_widget_show(if_cb);
198 g_free(if_list->data);
199 if_list = g_list_remove_link(if_list, if_list);
203 count_hb = gtk_hbox_new(FALSE, 3);
204 gtk_container_add(GTK_CONTAINER(main_vb), count_hb);
205 gtk_widget_show(count_hb);
207 count_lb = gtk_label_new("Count:");
208 gtk_box_pack_start(GTK_BOX(count_hb), count_lb, FALSE, FALSE, 0);
209 gtk_widget_show(count_lb);
211 count_list = g_list_append(count_list, count_item1);
213 snprintf(count_item2, 15, "%d", cf.count);
214 count_list = g_list_append(count_list, count_item2);
217 count_cb = gtk_combo_new();
218 gtk_combo_set_popdown_strings(GTK_COMBO(count_cb), count_list);
219 gtk_box_pack_start(GTK_BOX(count_hb), count_cb, FALSE, FALSE, 0);
220 gtk_widget_show(count_cb);
223 count_list = g_list_remove_link(count_list, count_list);
226 filter_hb = gtk_hbox_new(FALSE, 3);
227 gtk_container_add(GTK_CONTAINER(main_vb), filter_hb);
228 gtk_widget_show(filter_hb);
230 filter_bt = gtk_button_new_with_label("Filter:");
231 gtk_signal_connect(GTK_OBJECT(filter_bt), "clicked",
232 GTK_SIGNAL_FUNC(prefs_cb), (gpointer) E_PR_PG_FILTER);
233 gtk_box_pack_start(GTK_BOX(filter_hb), filter_bt, FALSE, TRUE, 0);
234 gtk_widget_show(filter_bt);
236 filter_te = gtk_entry_new();
237 if (cf.cfilter) gtk_entry_set_text(GTK_ENTRY(filter_te), cf.cfilter);
238 gtk_object_set_data(GTK_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
239 gtk_box_pack_start(GTK_BOX(filter_hb), filter_te, TRUE, TRUE, 0);
240 gtk_widget_show(filter_te);
242 /* Misc row: Capture file checkbox and snap spinbutton */
243 caplen_hb = gtk_hbox_new(FALSE, 3);
244 gtk_container_add(GTK_CONTAINER(main_vb), caplen_hb);
245 gtk_widget_show(caplen_hb);
247 snap_lb = gtk_label_new("Capture length");
248 gtk_misc_set_alignment(GTK_MISC(snap_lb), 0, 0.5);
249 gtk_box_pack_start(GTK_BOX(caplen_hb), snap_lb, FALSE, FALSE, 6);
250 gtk_widget_show(snap_lb);
252 adj = (GtkAdjustment *) gtk_adjustment_new((float) cf.snap,
253 MIN_PACKET_SIZE, MAX_PACKET_SIZE, 1.0, 10.0, 0.0);
254 snap_sb = gtk_spin_button_new (adj, 0, 0);
255 gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (snap_sb), TRUE);
256 gtk_widget_set_usize (snap_sb, 80, 0);
257 gtk_box_pack_start (GTK_BOX(caplen_hb), snap_sb, FALSE, FALSE, 3);
258 gtk_widget_show(snap_sb);
260 /* Button row: OK and cancel buttons */
261 bbox = gtk_hbutton_box_new();
262 gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
263 gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
264 gtk_container_add(GTK_CONTAINER(main_vb), bbox);
265 gtk_widget_show(bbox);
267 ok_bt = gtk_button_new_with_label ("OK");
268 gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked",
269 GTK_SIGNAL_FUNC(capture_prep_ok_cb), GTK_OBJECT(cap_open_w));
270 GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
271 gtk_box_pack_start (GTK_BOX (bbox), ok_bt, TRUE, TRUE, 0);
272 gtk_widget_grab_default(ok_bt);
273 gtk_widget_show(ok_bt);
275 cancel_bt = gtk_button_new_with_label ("Cancel");
276 gtk_signal_connect(GTK_OBJECT(cancel_bt), "clicked",
277 GTK_SIGNAL_FUNC(capture_prep_close_cb), GTK_OBJECT(cap_open_w));
278 GTK_WIDGET_SET_FLAGS(cancel_bt, GTK_CAN_DEFAULT);
279 gtk_box_pack_start (GTK_BOX (bbox), cancel_bt, TRUE, TRUE, 0);
280 gtk_widget_show(cancel_bt);
282 /* Attach pointers to needed widgets to the capture prefs window/object */
283 gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_IFACE_KEY, if_cb);
284 gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_FILT_KEY, filter_te);
285 gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_COUNT_KEY, count_cb);
286 gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_SNAP_KEY, snap_sb);
288 gtk_widget_show(cap_open_w);
292 capture_prep_ok_cb(GtkWidget *ok_bt, gpointer parent_w) {
293 GtkWidget *if_cb, *filter_te, *count_cb, *snap_sb;
297 if_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_IFACE_KEY);
298 filter_te = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_FILT_KEY);
299 count_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_COUNT_KEY);
300 snap_sb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_SNAP_KEY);
302 if (cf.iface) g_free(cf.iface);
304 g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry)));
306 filter_text = gtk_entry_get_text(GTK_ENTRY(filter_te));
307 if (cf.cfilter) g_free(cf.cfilter);
308 if (filter_text && filter_text[0]) {
309 cf.cfilter = g_strdup(gtk_entry_get_text(GTK_ENTRY(filter_te)));
311 cf.count = atoi(gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(count_cb)->entry)));
312 cf.snap = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(snap_sb));
314 cf.snap = MAX_PACKET_SIZE;
315 else if (cf.snap < MIN_PACKET_SIZE)
316 cf.snap = MIN_PACKET_SIZE;
318 gtk_widget_destroy(GTK_WIDGET(parent_w));
320 /* Choose a random name for the capture buffer */
321 if (cf.save_file && !cf.user_saved) {
322 unlink(cf.save_file); /* silently ignore error */
323 g_free(cf.save_file);
325 cf.save_file = tempnam(NULL, "ether");
328 if( fork_mode ){ /* use fork() for capture */
331 char scount[24]; /* need a constant for len of numbers */
334 sprintf(ssnap,"%d",cf.snap); /* in liu of itoa */
335 sprintf(scount,"%d",cf.count);
336 signal(SIGCHLD, SIG_IGN);
337 if (sync_mode) pipe(sync_pipe);
338 if((fork_child = fork()) == 0){
339 /* args: -k -- capture
340 * -i interface specification
342 * -c count to capture
343 * -Q quit after capture (forces -k)
347 * -f "filter expression"
353 execl(ethereal_path,"ethereal","-k","-Q","-i",cf.iface,"-w",cf.save_file,
354 "-c", scount, "-s", ssnap, "-S",
355 "-m", medium_font, "-b", bold_font,
356 (cf.cfilter == NULL)? 0 : "-f", (cf.cfilter == NULL)? 0 : cf.cfilter,
360 execl(ethereal_path,"ethereal","-k","-Q","-i",cf.iface,"-w",cf.save_file,
361 "-c", scount, "-s", ssnap,
362 "-m", medium_font, "-b", bold_font,
363 (cf.cfilter == NULL)? 0 : "-f", (cf.cfilter == NULL)? 0 : cf.cfilter,
368 cf.filename = cf.save_file;
371 while (!sigusr2_received) {
372 struct timeval timeout = {1,0};
373 select(0, NULL, NULL, NULL, &timeout);
374 if (kill(fork_child, 0) == -1 && errno == ESRCH)
377 if (sigusr2_received) {
378 err = tail_cap_file(cf.save_file, &cf);
380 simple_dialog(ESD_TYPE_WARN, NULL,
381 file_open_error_message(err, FALSE), cf.save_file);
384 sigusr2_received = FALSE;
393 capture_prep_close_cb(GtkWidget *close_bt, gpointer parent_w)
395 gtk_grab_remove(GTK_WIDGET(parent_w));
396 gtk_widget_destroy(GTK_WIDGET(parent_w));
401 GtkWidget *cap_w, *main_vb, *count_lb, *tcp_lb, *udp_lb,
402 *ospf_lb, *gre_lb, *other_lb, *stop_bt;
404 gchar err_str[PCAP_ERRBUF_SIZE], label_str[32];
406 bpf_u_int32 netnum, netmask;
407 time_t upd_time, cur_time;
413 ld.linktype = DLT_NULL;
423 close_cap_file(&cf, info_bar, file_ctx);
425 pch = pcap_open_live(cf.iface, cf.snap, 1, 250, err_str);
428 ld.pdh = pcap_dump_open(pch, cf.save_file);
430 if (ld.pdh == NULL) { /* We have an error */
431 snprintf(err_str, PCAP_ERRBUF_SIZE, "Error trying to save capture to "
432 "file:\n%s", pcap_geterr(pch));
433 simple_dialog(ESD_TYPE_WARN, NULL, err_str);
438 ld.linktype = pcap_datalink(pch);
441 if (pcap_lookupnet (cf.iface, &netnum, &netmask, err_str) < 0) {
442 simple_dialog(ESD_TYPE_WARN, NULL,
443 "Can't use filter: Couldn't obtain netmask info.");
445 } else if (pcap_compile(pch, &cf.fcode, cf.cfilter, 1, netmask) < 0) {
446 simple_dialog(ESD_TYPE_WARN, NULL, "Unable to parse filter string.");
448 } else if (pcap_setfilter(pch, &cf.fcode) < 0) {
449 simple_dialog(ESD_TYPE_WARN, NULL, "Can't install filter.");
454 cap_w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
455 gtk_window_set_title(GTK_WINDOW(cap_w), "Ethereal: Capture / Playback");
457 /* Container for capture display widgets */
458 main_vb = gtk_vbox_new(FALSE, 1);
459 gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
460 gtk_container_add(GTK_CONTAINER(cap_w), main_vb);
461 gtk_widget_show(main_vb);
463 count_lb = gtk_label_new("Count: 0");
464 gtk_box_pack_start(GTK_BOX(main_vb), count_lb, FALSE, FALSE, 3);
465 gtk_widget_show(count_lb);
467 tcp_lb = gtk_label_new("TCP: 0 (0.0%)");
468 gtk_box_pack_start(GTK_BOX(main_vb), tcp_lb, FALSE, FALSE, 3);
469 gtk_widget_show(tcp_lb);
471 udp_lb = gtk_label_new("UDP: 0 (0.0%)");
472 gtk_box_pack_start(GTK_BOX(main_vb), udp_lb, FALSE, FALSE, 3);
473 gtk_widget_show(udp_lb);
475 ospf_lb = gtk_label_new("OSPF: 0 (0.0%)");
476 gtk_box_pack_start(GTK_BOX(main_vb), ospf_lb, FALSE, FALSE, 3);
477 gtk_widget_show(ospf_lb);
479 gre_lb = gtk_label_new("GRE: 0 (0.0%)");
480 gtk_box_pack_start(GTK_BOX(main_vb), gre_lb, FALSE, FALSE, 3);
481 gtk_widget_show(gre_lb);
483 other_lb = gtk_label_new("Other: 0 (0.0%)");
484 gtk_box_pack_start(GTK_BOX(main_vb), other_lb, FALSE, FALSE, 3);
485 gtk_widget_show(other_lb);
487 stop_bt = gtk_button_new_with_label ("Stop");
488 gtk_signal_connect(GTK_OBJECT(stop_bt), "clicked",
489 GTK_SIGNAL_FUNC(capture_stop_cb), (gpointer) &ld);
490 gtk_box_pack_end(GTK_BOX(main_vb), stop_bt, FALSE, FALSE, 3);
491 GTK_WIDGET_SET_FLAGS(stop_bt, GTK_CAN_DEFAULT);
492 gtk_widget_grab_default(stop_bt);
493 GTK_WIDGET_SET_FLAGS(stop_bt, GTK_CAN_DEFAULT);
494 gtk_widget_grab_default(stop_bt);
495 gtk_widget_show(stop_bt);
497 gtk_widget_show(cap_w);
500 upd_time = time(NULL);
502 while (gtk_events_pending()) gtk_main_iteration();
503 pcap_dispatch(pch, 1, capture_pcap_cb, (u_char *) &ld);
505 /* Only update once a second so as not to overload slow displays */
506 cur_time = time(NULL);
507 if (cur_time > upd_time) {
511 sprintf(label_str, "Count: %d", ld.counts.total);
512 gtk_label_set(GTK_LABEL(count_lb), label_str);
514 sprintf(label_str, "TCP: %d (%.1f%%)", ld.counts.tcp,
515 pct(ld.counts.tcp, ld.counts.total));
516 gtk_label_set(GTK_LABEL(tcp_lb), label_str);
518 sprintf(label_str, "UDP: %d (%.1f%%)", ld.counts.udp,
519 pct(ld.counts.udp, ld.counts.total));
520 gtk_label_set(GTK_LABEL(udp_lb), label_str);
522 sprintf(label_str, "OSPF: %d (%.1f%%)", ld.counts.ospf,
523 pct(ld.counts.ospf, ld.counts.total));
524 gtk_label_set(GTK_LABEL(ospf_lb), label_str);
526 sprintf(label_str, "GRE: %d (%.1f%%)", ld.counts.gre,
527 pct(ld.counts.gre, ld.counts.total));
528 gtk_label_set(GTK_LABEL(gre_lb), label_str);
530 sprintf(label_str, "Other: %d (%.1f%%)", ld.counts.other,
531 pct(ld.counts.other, ld.counts.total));
532 gtk_label_set(GTK_LABEL(other_lb), label_str);
536 if (ld.pdh) pcap_dump_close(ld.pdh);
539 gtk_grab_remove(GTK_WIDGET(cap_w));
540 gtk_widget_destroy(GTK_WIDGET(cap_w));
542 while (gtk_events_pending()) gtk_main_iteration();
543 simple_dialog(ESD_TYPE_WARN, NULL,
544 "The capture session could not be initiated. Please\n"
545 "check to make sure you have sufficient permissions, and\n"
546 "that you have the proper interface specified.");
549 if( quit_after_cap ){
550 /* DON'T unlink the save file. Presumably someone wants it. */
555 err = load_cap_file(cf.save_file, &cf);
557 simple_dialog(ESD_TYPE_WARN, NULL,
558 file_open_error_message(err, FALSE), cf.save_file);
562 set_menu_sensitivity("/File/Save", TRUE);
563 set_menu_sensitivity("/File/Save as", FALSE);
564 set_menu_sensitivity("/Tools/Summary", TRUE);
566 set_menu_sensitivity("<Main>/File/Save", TRUE);
567 set_menu_sensitivity("<Main>/File/Save as", FALSE);
568 set_menu_sensitivity("<Main>/Tools/Summary", TRUE);
573 pct(gint num, gint denom) {
575 return (float) num * 100.0 / (float) denom;
582 capture_stop_cb(GtkWidget *w, gpointer data) {
583 loop_data *ld = (loop_data *) data;
589 capture_pcap_cb(u_char *user, const struct pcap_pkthdr *phdr,
592 loop_data *ld = (loop_data *) user;
593 time_t *sync_time= &ld->sync_time, cur_time;
595 if ((++ld->counts.total >= ld->max) && (ld->max > 0))
599 /* Currently, pcap_dumper_t is a FILE *. Let's hope that doesn't change. */
600 if (ld->pdh) pcap_dump((u_char *) ld->pdh, phdr, pd);
602 cur_time = time(NULL);
603 if (cur_time > *sync_time) {
604 /* sync every seconds */
605 *sync_time = cur_time;
606 fflush((FILE *)ld->pdh);
611 switch (ld->linktype) {
613 capture_eth(pd, phdr->caplen, &ld->counts);
616 capture_fddi(pd, phdr->caplen, &ld->counts);
619 capture_tr(pd, phdr->caplen, &ld->counts);
622 capture_null(pd, phdr->caplen, &ld->counts);
625 capture_ppp(pd, phdr->caplen, &ld->counts);
628 capture_raw(pd, phdr->caplen, &ld->counts);
632 if (sync_mode && !ld->signal_sent) {
633 /* will trigger the father to open the cap file which contains
634 at least one complete packet */
635 fflush((FILE *)ld->pdh);
636 kill(getppid(), SIGUSR2);
642 #endif /* HAVE_LIBPCAP */