2 * Routines for packet capture windows
4 * $Id: capture.c,v 1.43 1999/08/10 11:08:38 deniel 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>
37 #ifdef HAVE_SYS_STAT_H
38 # include <sys/stat.h>
52 #ifdef HAVE_SYS_SOCKET_H
53 #include <sys/socket.h>
56 #ifdef HAVE_SYS_IOCTL_H
57 #include <sys/ioctl.h>
67 #ifdef NEED_SNPRINTF_H
73 # include "snprintf.h"
76 #ifdef HAVE_SYS_SOCKIO_H
77 # include <sys/sockio.h>
89 extern capture_file cf;
90 extern GtkWidget *info_bar;
91 extern guint file_ctx;
93 extern gchar *ethereal_path;
94 extern gchar *medium_font;
95 extern gchar *bold_font;
97 extern int sync_pipe[];
99 extern int sigusr2_received;
100 extern int quit_after_cap;
102 /* Capture callback data keys */
103 #define E_CAP_IFACE_KEY "cap_iface"
104 #define E_CAP_FILT_KEY "cap_filter"
105 #define E_CAP_COUNT_KEY "cap_count"
106 #define E_CAP_OPEN_KEY "cap_open"
107 #define E_CAP_SNAP_KEY "cap_snap"
109 /* Capture filter key */
110 #define E_CAP_FILT_TE_KEY "cap_filt_te"
112 static void capture_prep_ok_cb(GtkWidget *, gpointer);
113 static void capture_prep_close_cb(GtkWidget *, gpointer);
114 static float pct(gint, gint);
115 static void capture_stop_cb(GtkWidget *, gpointer);
116 static void capture_pcap_cb(u_char *, const struct pcap_pkthdr *,
120 get_interface_list() {
122 struct ifreq *ifr, *last;
124 int sock = socket(AF_INET, SOCK_DGRAM, 0);
128 simple_dialog(ESD_TYPE_WARN, NULL,
129 "Can't list interfaces: error opening socket.");
133 /* Since we have to grab the interface list all at once, we'll make
135 ifc.ifc_len = 1024 * sizeof(struct ifreq);
136 ifc.ifc_buf = malloc(ifc.ifc_len);
138 if (ioctl(sock, SIOCGIFCONF, &ifc) < 0 ||
139 ifc.ifc_len < sizeof(struct ifreq))
141 simple_dialog(ESD_TYPE_WARN, NULL,
142 "Can't list interfaces: ioctl error.");
146 ifr = (struct ifreq *) ifc.ifc_req;
147 last = (struct ifreq *) ((char *) ifr + ifc.ifc_len);
152 * - Interfaces that are up, and not loopback
153 * - IP interfaces (do we really need this?)
154 * - Anything that doesn't begin with "lo" (loopback again) or "dummy"
155 * - Anything that doesn't include a ":" (Solaris virtuals)
157 if (! (ifr->ifr_flags & (IFF_UP | IFF_LOOPBACK)) &&
158 (ifr->ifr_addr.sa_family == AF_INET) &&
159 strncmp(ifr->ifr_name, "lo", 2) &&
160 strncmp(ifr->ifr_name, "dummy", 5) &&
161 ! strchr(ifr->ifr_name, ':')) {
162 il = g_list_append(il, g_strdup(ifr->ifr_name));
165 ifr = (struct ifreq *) ((char *) ifr + ifr->ifr_addr.sa_len + IFNAMSIZ);
167 ifr = (struct ifreq *) ((char *) ifr + sizeof(struct ifreq));
176 capture_prep_cb(GtkWidget *w, gpointer d) {
177 GtkWidget *cap_open_w, *if_cb, *if_lb,
178 *count_lb, *count_cb, *main_vb, *if_hb, *count_hb,
179 *filter_hb, *filter_bt, *filter_te, *caplen_hb,
180 *bbox, *ok_bt, *cancel_bt, *snap_lb,
183 GList *if_list, *count_list = NULL;
184 gchar *count_item1 = "0 (Infinite)", count_item2[16];
186 cap_open_w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
187 gtk_window_set_title(GTK_WINDOW(cap_open_w), "Ethereal: Capture Preferences");
189 /* Container for each row of widgets */
190 main_vb = gtk_vbox_new(FALSE, 3);
191 gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
192 gtk_container_add(GTK_CONTAINER(cap_open_w), main_vb);
193 gtk_widget_show(main_vb);
196 if_hb = gtk_hbox_new(FALSE, 3);
197 gtk_container_add(GTK_CONTAINER(main_vb), if_hb);
198 gtk_widget_show(if_hb);
200 if_lb = gtk_label_new("Interface:");
201 gtk_box_pack_start(GTK_BOX(if_hb), if_lb, FALSE, FALSE, 0);
202 gtk_widget_show(if_lb);
204 if_list = get_interface_list();
206 if_cb = gtk_combo_new();
207 gtk_combo_set_popdown_strings(GTK_COMBO(if_cb), if_list);
209 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry), cf.iface);
211 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry), if_list->data);
212 gtk_box_pack_start(GTK_BOX(if_hb), if_cb, FALSE, FALSE, 0);
213 gtk_widget_show(if_cb);
216 g_free(if_list->data);
217 if_list = g_list_remove_link(if_list, if_list);
221 count_hb = gtk_hbox_new(FALSE, 3);
222 gtk_container_add(GTK_CONTAINER(main_vb), count_hb);
223 gtk_widget_show(count_hb);
225 count_lb = gtk_label_new("Count:");
226 gtk_box_pack_start(GTK_BOX(count_hb), count_lb, FALSE, FALSE, 0);
227 gtk_widget_show(count_lb);
229 count_list = g_list_append(count_list, count_item1);
231 snprintf(count_item2, 15, "%d", cf.count);
232 count_list = g_list_append(count_list, count_item2);
235 count_cb = gtk_combo_new();
236 gtk_combo_set_popdown_strings(GTK_COMBO(count_cb), count_list);
237 gtk_box_pack_start(GTK_BOX(count_hb), count_cb, FALSE, FALSE, 0);
238 gtk_widget_show(count_cb);
241 count_list = g_list_remove_link(count_list, count_list);
244 filter_hb = gtk_hbox_new(FALSE, 3);
245 gtk_container_add(GTK_CONTAINER(main_vb), filter_hb);
246 gtk_widget_show(filter_hb);
248 filter_bt = gtk_button_new_with_label("Filter:");
249 gtk_signal_connect(GTK_OBJECT(filter_bt), "clicked",
250 GTK_SIGNAL_FUNC(prefs_cb), (gpointer) E_PR_PG_FILTER);
251 gtk_box_pack_start(GTK_BOX(filter_hb), filter_bt, FALSE, TRUE, 0);
252 gtk_widget_show(filter_bt);
254 filter_te = gtk_entry_new();
255 if (cf.cfilter) gtk_entry_set_text(GTK_ENTRY(filter_te), cf.cfilter);
256 gtk_object_set_data(GTK_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
257 gtk_box_pack_start(GTK_BOX(filter_hb), filter_te, TRUE, TRUE, 0);
258 gtk_widget_show(filter_te);
260 /* Misc row: Capture file checkbox and snap spinbutton */
261 caplen_hb = gtk_hbox_new(FALSE, 3);
262 gtk_container_add(GTK_CONTAINER(main_vb), caplen_hb);
263 gtk_widget_show(caplen_hb);
265 snap_lb = gtk_label_new("Capture length");
266 gtk_misc_set_alignment(GTK_MISC(snap_lb), 0, 0.5);
267 gtk_box_pack_start(GTK_BOX(caplen_hb), snap_lb, FALSE, FALSE, 6);
268 gtk_widget_show(snap_lb);
270 adj = (GtkAdjustment *) gtk_adjustment_new((float) cf.snap,
271 MIN_PACKET_SIZE, MAX_PACKET_SIZE, 1.0, 10.0, 0.0);
272 snap_sb = gtk_spin_button_new (adj, 0, 0);
273 gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (snap_sb), TRUE);
274 gtk_widget_set_usize (snap_sb, 80, 0);
275 gtk_box_pack_start (GTK_BOX(caplen_hb), snap_sb, FALSE, FALSE, 3);
276 gtk_widget_show(snap_sb);
278 /* Button row: OK and cancel buttons */
279 bbox = gtk_hbutton_box_new();
280 gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
281 gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
282 gtk_container_add(GTK_CONTAINER(main_vb), bbox);
283 gtk_widget_show(bbox);
285 ok_bt = gtk_button_new_with_label ("OK");
286 gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked",
287 GTK_SIGNAL_FUNC(capture_prep_ok_cb), GTK_OBJECT(cap_open_w));
288 GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
289 gtk_box_pack_start (GTK_BOX (bbox), ok_bt, TRUE, TRUE, 0);
290 gtk_widget_grab_default(ok_bt);
291 gtk_widget_show(ok_bt);
293 cancel_bt = gtk_button_new_with_label ("Cancel");
294 gtk_signal_connect(GTK_OBJECT(cancel_bt), "clicked",
295 GTK_SIGNAL_FUNC(capture_prep_close_cb), GTK_OBJECT(cap_open_w));
296 GTK_WIDGET_SET_FLAGS(cancel_bt, GTK_CAN_DEFAULT);
297 gtk_box_pack_start (GTK_BOX (bbox), cancel_bt, TRUE, TRUE, 0);
298 gtk_widget_show(cancel_bt);
300 /* Attach pointers to needed widgets to the capture prefs window/object */
301 gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_IFACE_KEY, if_cb);
302 gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_FILT_KEY, filter_te);
303 gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_COUNT_KEY, count_cb);
304 gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_SNAP_KEY, snap_sb);
306 gtk_widget_show(cap_open_w);
310 capture_prep_ok_cb(GtkWidget *ok_bt, gpointer parent_w) {
311 GtkWidget *if_cb, *filter_te, *count_cb, *snap_sb;
315 if_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_IFACE_KEY);
316 filter_te = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_FILT_KEY);
317 count_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_COUNT_KEY);
318 snap_sb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_SNAP_KEY);
320 if (cf.iface) g_free(cf.iface);
322 g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry)));
324 filter_text = gtk_entry_get_text(GTK_ENTRY(filter_te));
325 if (cf.cfilter) g_free(cf.cfilter);
326 cf.cfilter = NULL; /* ead 06/16/99 */
327 if (filter_text && filter_text[0]) {
328 cf.cfilter = g_strdup(gtk_entry_get_text(GTK_ENTRY(filter_te)));
330 cf.count = atoi(gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(count_cb)->entry)));
331 cf.snap = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(snap_sb));
333 cf.snap = MAX_PACKET_SIZE;
334 else if (cf.snap < MIN_PACKET_SIZE)
335 cf.snap = MIN_PACKET_SIZE;
337 gtk_widget_destroy(GTK_WIDGET(parent_w));
339 /* Choose a random name for the capture buffer */
340 if (cf.save_file && !cf.user_saved) {
341 unlink(cf.save_file); /* silently ignore error */
342 g_free(cf.save_file);
344 cf.save_file = tempnam(NULL, "ether");
347 if( fork_mode ){ /* use fork() for capture */
350 char scount[24]; /* need a constant for len of numbers */
353 sprintf(ssnap,"%d",cf.snap); /* in liu of itoa */
354 sprintf(scount,"%d",cf.count);
355 signal(SIGCHLD, SIG_IGN);
356 if (sync_mode) pipe(sync_pipe);
357 if((fork_child = fork()) == 0){
358 /* args: -k -- capture
359 * -i interface specification
361 * -c count to capture
362 * -Q quit after capture (forces -k)
366 * -f "filter expression"
372 execlp(ethereal_path,"ethereal","-k","-Q","-i",cf.iface,"-w",cf.save_file,
373 "-c", scount, "-s", ssnap, "-S",
374 "-m", medium_font, "-b", bold_font,
375 (cf.cfilter == NULL)? 0 : "-f", (cf.cfilter == NULL)? 0 : cf.cfilter,
379 execlp(ethereal_path,"ethereal","-k","-Q","-i",cf.iface,"-w",cf.save_file,
380 "-c", scount, "-s", ssnap,
381 "-m", medium_font, "-b", bold_font,
382 (cf.cfilter == NULL)? 0 : "-f", (cf.cfilter == NULL)? 0 : cf.cfilter,
387 cf.filename = cf.save_file;
390 while (!sigusr2_received) {
391 struct timeval timeout = {1,0};
392 select(0, NULL, NULL, NULL, &timeout);
393 if (kill(fork_child, 0) == -1 && errno == ESRCH)
396 if (sigusr2_received) {
397 err = tail_cap_file(cf.save_file, &cf);
399 simple_dialog(ESD_TYPE_WARN, NULL,
400 file_open_error_message(err, FALSE), cf.save_file);
403 sigusr2_received = FALSE;
412 capture_prep_close_cb(GtkWidget *close_bt, gpointer parent_w)
414 gtk_grab_remove(GTK_WIDGET(parent_w));
415 gtk_widget_destroy(GTK_WIDGET(parent_w));
420 GtkWidget *cap_w, *main_vb, *count_lb, *tcp_lb, *udp_lb,
421 *ospf_lb, *gre_lb, *other_lb, *stop_bt;
423 gchar err_str[PCAP_ERRBUF_SIZE], label_str[32];
425 bpf_u_int32 netnum, netmask;
426 time_t upd_time, cur_time;
432 ld.linktype = DLT_NULL;
441 close_cap_file(&cf, info_bar, file_ctx);
443 pch = pcap_open_live(cf.iface, cf.snap, 1, 250, err_str);
446 /* save the old new umask and set the new one to readable only by the user */
447 mode_t old_umask = umask(0066);
449 /* Have libpcap create the empty dumpfile */
450 ld.pdh = pcap_dump_open(pch, cf.save_file);
452 /* reset the umask to the original value */
453 (void) umask(old_umask);
455 if (ld.pdh == NULL) { /* We have an error */
456 snprintf(err_str, PCAP_ERRBUF_SIZE, "Error trying to save capture to "
457 "file:\n%s", pcap_geterr(pch));
458 simple_dialog(ESD_TYPE_WARN, NULL, err_str);
463 ld.linktype = pcap_datalink(pch);
466 if (pcap_lookupnet (cf.iface, &netnum, &netmask, err_str) < 0) {
467 simple_dialog(ESD_TYPE_WARN, NULL,
468 "Can't use filter: Couldn't obtain netmask info.");
469 pcap_dump_close(ld.pdh);
470 unlink(cf.save_file); /* silently ignore error */
473 } else if (pcap_compile(pch, &cf.fcode, cf.cfilter, 1, netmask) < 0) {
474 simple_dialog(ESD_TYPE_WARN, NULL, "Unable to parse filter string.");
475 pcap_dump_close(ld.pdh);
476 unlink(cf.save_file); /* silently ignore error */
479 } else if (pcap_setfilter(pch, &cf.fcode) < 0) {
480 simple_dialog(ESD_TYPE_WARN, NULL, "Can't install filter.");
481 pcap_dump_close(ld.pdh);
482 unlink(cf.save_file); /* silently ignore error */
489 /* Sync out the capture file, so the header makes it to the file
490 system, and signal our parent so that they'll open the capture
491 file and update its windows to indicate that we have a live
492 capture in progress. */
493 fflush((FILE *)ld.pdh);
494 kill(getppid(), SIGUSR2);
497 cap_w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
498 gtk_window_set_title(GTK_WINDOW(cap_w), "Ethereal: Capture / Playback");
500 /* Container for capture display widgets */
501 main_vb = gtk_vbox_new(FALSE, 1);
502 gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
503 gtk_container_add(GTK_CONTAINER(cap_w), main_vb);
504 gtk_widget_show(main_vb);
506 count_lb = gtk_label_new("Count: 0");
507 gtk_box_pack_start(GTK_BOX(main_vb), count_lb, FALSE, FALSE, 3);
508 gtk_widget_show(count_lb);
510 tcp_lb = gtk_label_new("TCP: 0 (0.0%)");
511 gtk_box_pack_start(GTK_BOX(main_vb), tcp_lb, FALSE, FALSE, 3);
512 gtk_widget_show(tcp_lb);
514 udp_lb = gtk_label_new("UDP: 0 (0.0%)");
515 gtk_box_pack_start(GTK_BOX(main_vb), udp_lb, FALSE, FALSE, 3);
516 gtk_widget_show(udp_lb);
518 ospf_lb = gtk_label_new("OSPF: 0 (0.0%)");
519 gtk_box_pack_start(GTK_BOX(main_vb), ospf_lb, FALSE, FALSE, 3);
520 gtk_widget_show(ospf_lb);
522 gre_lb = gtk_label_new("GRE: 0 (0.0%)");
523 gtk_box_pack_start(GTK_BOX(main_vb), gre_lb, FALSE, FALSE, 3);
524 gtk_widget_show(gre_lb);
526 other_lb = gtk_label_new("Other: 0 (0.0%)");
527 gtk_box_pack_start(GTK_BOX(main_vb), other_lb, FALSE, FALSE, 3);
528 gtk_widget_show(other_lb);
530 stop_bt = gtk_button_new_with_label ("Stop");
531 gtk_signal_connect(GTK_OBJECT(stop_bt), "clicked",
532 GTK_SIGNAL_FUNC(capture_stop_cb), (gpointer) &ld);
533 gtk_box_pack_end(GTK_BOX(main_vb), stop_bt, FALSE, FALSE, 3);
534 GTK_WIDGET_SET_FLAGS(stop_bt, GTK_CAN_DEFAULT);
535 gtk_widget_grab_default(stop_bt);
536 GTK_WIDGET_SET_FLAGS(stop_bt, GTK_CAN_DEFAULT);
537 gtk_widget_grab_default(stop_bt);
538 gtk_widget_show(stop_bt);
540 gtk_widget_show(cap_w);
543 upd_time = time(NULL);
545 while (gtk_events_pending()) gtk_main_iteration();
546 inpkts = pcap_dispatch(pch, 1, capture_pcap_cb, (u_char *) &ld);
549 /* Only update once a second so as not to overload slow displays */
550 cur_time = time(NULL);
551 if (cur_time > upd_time) {
555 sprintf(label_str, "Count: %d", ld.counts.total);
556 gtk_label_set(GTK_LABEL(count_lb), label_str);
558 sprintf(label_str, "TCP: %d (%.1f%%)", ld.counts.tcp,
559 pct(ld.counts.tcp, ld.counts.total));
560 gtk_label_set(GTK_LABEL(tcp_lb), label_str);
562 sprintf(label_str, "UDP: %d (%.1f%%)", ld.counts.udp,
563 pct(ld.counts.udp, ld.counts.total));
564 gtk_label_set(GTK_LABEL(udp_lb), label_str);
566 sprintf(label_str, "OSPF: %d (%.1f%%)", ld.counts.ospf,
567 pct(ld.counts.ospf, ld.counts.total));
568 gtk_label_set(GTK_LABEL(ospf_lb), label_str);
570 sprintf(label_str, "GRE: %d (%.1f%%)", ld.counts.gre,
571 pct(ld.counts.gre, ld.counts.total));
572 gtk_label_set(GTK_LABEL(gre_lb), label_str);
574 sprintf(label_str, "Other: %d (%.1f%%)", ld.counts.other,
575 pct(ld.counts.other, ld.counts.total));
576 gtk_label_set(GTK_LABEL(other_lb), label_str);
578 /* do sync here, too */
579 fflush((FILE *)ld.pdh);
580 if (sync_mode && ld.sync_packets) {
582 sprintf(tmp, "%d*", ld.sync_packets);
583 write(1, tmp, strlen(tmp));
589 if (ld.pdh) pcap_dump_close(ld.pdh);
592 gtk_grab_remove(GTK_WIDGET(cap_w));
593 gtk_widget_destroy(GTK_WIDGET(cap_w));
595 while (gtk_events_pending()) gtk_main_iteration();
596 simple_dialog(ESD_TYPE_WARN, NULL,
597 "The capture session could not be initiated. Please\n"
598 "check to make sure you have sufficient permissions, and\n"
599 "that you have the proper interface specified.");
602 if( quit_after_cap ){
603 /* DON'T unlink the save file. Presumably someone wants it. */
608 /* "pch" is non-NULL only if we successfully started a capture.
609 If we haven't, there's no capture file to load. */
610 err = load_cap_file(cf.save_file, NULL, &cf);
612 set_menu_sensitivity("/File/Save", TRUE);
613 set_menu_sensitivity("/File/Save As...", FALSE);
619 pct(gint num, gint denom) {
621 return (float) num * 100.0 / (float) denom;
628 capture_stop_cb(GtkWidget *w, gpointer data) {
629 loop_data *ld = (loop_data *) data;
635 capture_pcap_cb(u_char *user, const struct pcap_pkthdr *phdr,
638 loop_data *ld = (loop_data *) user;
640 if ((++ld->counts.total >= ld->max) && (ld->max > 0))
644 /* Currently, pcap_dumper_t is a FILE *. Let's hope that doesn't change. */
645 if (ld->pdh) pcap_dump((u_char *) ld->pdh, phdr, pd);
647 switch (ld->linktype) {
649 capture_eth(pd, phdr->caplen, &ld->counts);
652 capture_fddi(pd, phdr->caplen, &ld->counts);
655 capture_tr(pd, phdr->caplen, &ld->counts);
658 capture_null(pd, phdr->caplen, &ld->counts);
661 capture_ppp(pd, phdr->caplen, &ld->counts);
664 capture_raw(pd, phdr->caplen, &ld->counts);
669 #endif /* HAVE_LIBPCAP */