2 * Routines for packet capture windows
4 * $Id: capture.c,v 1.73 1999/09/30 06:11:43 guy 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>
53 #ifdef HAVE_SYS_SOCKET_H
54 #include <sys/socket.h>
57 #ifdef HAVE_SYS_IOCTL_H
58 #include <sys/ioctl.h>
68 #ifdef NEED_SNPRINTF_H
74 # include "snprintf.h"
86 static void capture_stop_cb(GtkWidget *, gpointer);
87 static void capture_pcap_cb(u_char *, const struct pcap_pkthdr *,
89 static float pct(gint, gint);
91 typedef struct _loop_data {
109 /* Choose a random name for the capture buffer */
110 cf.save_file_fd = create_tempfile(tmpname, sizeof tmpname, "ether");
111 if (cf.save_file_fd == -1) {
112 simple_dialog(ESD_TYPE_WARN, NULL,
113 "The file to which the capture would be saved (\"%s\")"
114 "could not be opened: %s.", tmpname, strerror(errno));
117 close_cap_file(&cf, info_bar, file_ctx);
118 if (cf.save_file != NULL) {
119 /* If the current file is a temporary capture file, remove it. */
121 unlink(cf.save_file); /* silently ignore error */
122 g_free(cf.save_file);
124 cf.save_file = g_strdup(tmpname);
127 if (sync_mode || fork_mode) { /* use fork() for capture */
130 char scount[24]; /* need a constant for len of numbers */
131 char save_file_fd[24];
134 sprintf(ssnap,"%d",cf.snap); /* in lieu of itoa */
135 sprintf(scount,"%d",cf.count);
136 sprintf(save_file_fd,"%d",cf.save_file_fd);
137 signal(SIGCHLD, SIG_IGN);
140 if ((fork_child = fork()) == 0) {
141 /* args: -k -- capture
142 * -i interface specification
144 * -W file descriptor to write
145 * -c count to capture
146 * -Q quit after capture (forces -k)
150 * -f "filter expression"
156 execlp(ethereal_path, CHILD_NAME, "-k", "-Q", "-i", cf.iface,
157 "-w", cf.save_file, "-W", save_file_fd,
158 "-c", scount, "-s", ssnap, "-S",
159 "-m", medium_font, "-b", bold_font,
160 (cf.cfilter == NULL)? 0 : "-f",
161 (cf.cfilter == NULL)? 0 : cf.cfilter,
165 execlp(ethereal_path, CHILD_NAME, "-k", "-Q", "-i", cf.iface,
166 "-w", cf.save_file, "-W", save_file_fd,
167 "-c", scount, "-s", ssnap,
168 "-m", medium_font, "-b", bold_font,
169 (cf.cfilter == NULL)? 0 : "-f",
170 (cf.cfilter == NULL)? 0 : cf.cfilter,
175 cf.filename = cf.save_file;
179 /* Read a byte count from "sync_pipe[0]", terminated with a
180 colon; if the count is 0, the child process created the
181 capture file and we should start reading from it, otherwise
182 the capture couldn't start and the count is a count of bytes
183 of error message, and we should display the message. */
186 i = read(sync_pipe[0], &c, 1);
188 /* EOF - the child process died.
189 XXX - reap it and report the status. */
190 simple_dialog(ESD_TYPE_WARN, NULL, "Capture child process died");
196 /* Child process handed us crap. */
197 simple_dialog(ESD_TYPE_WARN, NULL,
198 "Capture child process sent us a bad message");
201 byte_count = byte_count*10 + c - '0';
203 if (byte_count == 0) {
205 err = tail_cap_file(cf.save_file, &cf);
207 simple_dialog(ESD_TYPE_WARN, NULL,
208 file_open_error_message(err, FALSE), cf.save_file);
212 msg = g_malloc(byte_count + 1);
214 simple_dialog(ESD_TYPE_WARN, NULL,
215 "Capture child process failed, but its error message was too big.");
217 i = read(sync_pipe[0], msg, byte_count);
219 simple_dialog(ESD_TYPE_WARN, NULL,
220 "Capture child process failed: Error %s reading its error message.",
223 simple_dialog(ESD_TYPE_WARN, NULL,
224 "Capture child process failed: EOF reading its error message.");
226 simple_dialog(ESD_TYPE_WARN, NULL, msg);
240 GtkWidget *cap_w, *main_vb, *count_lb, *tcp_lb, *udp_lb, *icmp_lb,
241 *ospf_lb, *gre_lb, *netbios_lb, *other_lb, *stop_bt;
243 gchar err_str[PCAP_ERRBUF_SIZE], label_str[32];
245 bpf_u_int32 netnum, netmask;
246 time_t upd_time, cur_time;
253 ld.linktype = WTAP_ENCAP_UNKNOWN;
260 ld.counts.netbios = 0;
264 /* Open the network interface to capture from it. */
265 pch = pcap_open_live(cf.iface, cf.snap, 1, 250, err_str);
268 /* Well, we couldn't start the capture. */
269 if (!sync_mode && !fork_mode) {
270 /* In fork mode, we shouldn't do any UI stuff until we pop up the
271 capture-progress window, and, since we couldn't start the
272 capture, we haven't popped it up. */
273 while (gtk_events_pending()) gtk_main_iteration();
275 snprintf(errmsg, sizeof errmsg,
276 "The capture session could not be initiated (%s).\n"
277 "Please check to make sure you have sufficient permissions, and that\n"
278 "you have the proper interface specified.", err_str);
283 /* A capture filter was specified; set it up. */
284 if (pcap_lookupnet (cf.iface, &netnum, &netmask, err_str) < 0) {
285 snprintf(errmsg, sizeof errmsg,
286 "Can't use filter: Couldn't obtain netmask info (%s).", err_str);
289 if (pcap_compile(pch, &cf.fcode, cf.cfilter, 1, netmask) < 0) {
290 snprintf(errmsg, sizeof errmsg, "Unable to parse filter string (%s).",
294 if (pcap_setfilter(pch, &cf.fcode) < 0) {
295 snprintf(errmsg, sizeof errmsg, "Can't install filter (%s).",
301 /* Set up to write to the capture file. */
302 ld.linktype = wtap_pcap_encap_to_wtap_encap(pcap_datalink(pch));
303 if (ld.linktype == WTAP_ENCAP_UNKNOWN) {
304 strcpy(errmsg, "The network you're capturing from is of a type"
305 " that Ethereal doesn't support.");
308 ld.pdh = wtap_dump_fdopen(cf.save_file_fd, WTAP_FILE_PCAP,
309 ld.linktype, pcap_snapshot(pch), &err);
311 if (ld.pdh == NULL) {
312 /* We couldn't set up to write to the capture file. */
315 case WTAP_ERR_CANT_OPEN:
316 strcpy(errmsg, "The file to which the capture would be saved"
317 " couldn't be created for some unknown reason.");
320 case WTAP_ERR_SHORT_WRITE:
321 strcpy(errmsg, "A full header couldn't be written to the file"
322 " to which the capture would be saved.");
327 sprintf(errmsg, "The file to which the capture would be"
328 " saved (\"%s\") could not be opened: Error %d.",
331 sprintf(errmsg, "The file to which the capture would be"
332 " saved (\"%s\") could not be opened: %s.",
333 cf.save_file, strerror(err));
341 /* Well, we should be able to start capturing.
343 Sync out the capture file, so the header makes it to the file
344 system, and send a "capture started successfully and capture
345 file created" message to our parent so that they'll open the
346 capture file and update its windows to indicate that we have
347 a live capture in progress. */
348 fflush(wtap_dump_file(ld.pdh));
352 cap_w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
353 gtk_window_set_title(GTK_WINDOW(cap_w), "Ethereal: Capture / Playback");
355 /* Container for capture display widgets */
356 main_vb = gtk_vbox_new(FALSE, 1);
357 gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
358 gtk_container_add(GTK_CONTAINER(cap_w), main_vb);
359 gtk_widget_show(main_vb);
361 count_lb = gtk_label_new("Count: 0");
362 gtk_box_pack_start(GTK_BOX(main_vb), count_lb, FALSE, FALSE, 3);
363 gtk_widget_show(count_lb);
365 tcp_lb = gtk_label_new("TCP: 0 (0.0%)");
366 gtk_box_pack_start(GTK_BOX(main_vb), tcp_lb, FALSE, FALSE, 3);
367 gtk_widget_show(tcp_lb);
369 udp_lb = gtk_label_new("UDP: 0 (0.0%)");
370 gtk_box_pack_start(GTK_BOX(main_vb), udp_lb, FALSE, FALSE, 3);
371 gtk_widget_show(udp_lb);
373 icmp_lb = gtk_label_new("ICMP: 0 (0.0%)");
374 gtk_box_pack_start(GTK_BOX(main_vb), icmp_lb, FALSE, FALSE, 3);
375 gtk_widget_show(icmp_lb);
377 ospf_lb = gtk_label_new("OSPF: 0 (0.0%)");
378 gtk_box_pack_start(GTK_BOX(main_vb), ospf_lb, FALSE, FALSE, 3);
379 gtk_widget_show(ospf_lb);
381 gre_lb = gtk_label_new("GRE: 0 (0.0%)");
382 gtk_box_pack_start(GTK_BOX(main_vb), gre_lb, FALSE, FALSE, 3);
383 gtk_widget_show(gre_lb);
385 netbios_lb = gtk_label_new("NetBIOS: 0 (0.0%)");
386 gtk_box_pack_start(GTK_BOX(main_vb), netbios_lb, FALSE, FALSE, 3);
387 gtk_widget_show(netbios_lb);
389 other_lb = gtk_label_new("Other: 0 (0.0%)");
390 gtk_box_pack_start(GTK_BOX(main_vb), other_lb, FALSE, FALSE, 3);
391 gtk_widget_show(other_lb);
393 stop_bt = gtk_button_new_with_label ("Stop");
394 gtk_signal_connect(GTK_OBJECT(stop_bt), "clicked",
395 GTK_SIGNAL_FUNC(capture_stop_cb), (gpointer) &ld);
396 gtk_box_pack_end(GTK_BOX(main_vb), stop_bt, FALSE, FALSE, 3);
397 GTK_WIDGET_SET_FLAGS(stop_bt, GTK_CAN_DEFAULT);
398 gtk_widget_grab_default(stop_bt);
399 GTK_WIDGET_SET_FLAGS(stop_bt, GTK_CAN_DEFAULT);
400 gtk_widget_grab_default(stop_bt);
401 gtk_widget_show(stop_bt);
403 gtk_widget_show(cap_w);
406 upd_time = time(NULL);
408 while (gtk_events_pending()) gtk_main_iteration();
409 inpkts = pcap_dispatch(pch, 1, capture_pcap_cb, (u_char *) &ld);
411 ld.sync_packets += inpkts;
412 /* Only update once a second so as not to overload slow displays */
413 cur_time = time(NULL);
414 if (cur_time > upd_time) {
417 sprintf(label_str, "Count: %d", ld.counts.total);
418 gtk_label_set(GTK_LABEL(count_lb), label_str);
420 sprintf(label_str, "TCP: %d (%.1f%%)", ld.counts.tcp,
421 pct(ld.counts.tcp, ld.counts.total));
422 gtk_label_set(GTK_LABEL(tcp_lb), label_str);
424 sprintf(label_str, "UDP: %d (%.1f%%)", ld.counts.udp,
425 pct(ld.counts.udp, ld.counts.total));
426 gtk_label_set(GTK_LABEL(udp_lb), label_str);
428 sprintf(label_str, "ICMP: %d (%.1f%%)", ld.counts.icmp,
429 pct(ld.counts.icmp, ld.counts.total));
430 gtk_label_set(GTK_LABEL(icmp_lb), label_str);
432 sprintf(label_str, "OSPF: %d (%.1f%%)", ld.counts.ospf,
433 pct(ld.counts.ospf, ld.counts.total));
434 gtk_label_set(GTK_LABEL(ospf_lb), label_str);
436 sprintf(label_str, "GRE: %d (%.1f%%)", ld.counts.gre,
437 pct(ld.counts.gre, ld.counts.total));
438 gtk_label_set(GTK_LABEL(gre_lb), label_str);
440 sprintf(label_str, "NetBIOS: %d (%.1f%%)", ld.counts.netbios,
441 pct(ld.counts.netbios, ld.counts.total));
442 gtk_label_set(GTK_LABEL(netbios_lb), label_str);
444 sprintf(label_str, "Other: %d (%.1f%%)", ld.counts.other,
445 pct(ld.counts.other, ld.counts.total));
446 gtk_label_set(GTK_LABEL(other_lb), label_str);
448 /* do sync here, too */
449 fflush(wtap_dump_file(ld.pdh));
450 if (sync_mode && ld.sync_packets) {
452 sprintf(tmp, "%d*", ld.sync_packets);
453 write(1, tmp, strlen(tmp));
459 if (!wtap_dump_close(ld.pdh, &err)) {
460 /* XXX - in fork mode, this may not pop up, or, if it does,
461 it may disappear as soon as we exit.
463 We should have the parent process, while it's reading
464 the packet count update messages, catch error messages
465 and pop up a message box if it sees one. */
468 case WTAP_ERR_CANT_CLOSE:
469 simple_dialog(ESD_TYPE_WARN, NULL,
470 "The file to which the capture was being saved"
471 " couldn't be closed for some unknown reason.");
474 case WTAP_ERR_SHORT_WRITE:
475 simple_dialog(ESD_TYPE_WARN, NULL,
476 "Not all the data could be written to the file"
477 " to which the capture was being saved.");
482 simple_dialog(ESD_TYPE_WARN, NULL,
483 "The file to which the capture was being"
484 " saved (\"%s\") could not be closed: Error %d.",
487 simple_dialog(ESD_TYPE_WARN, NULL,
488 "The file to which the capture was being"
489 " saved (\"%s\") could not be closed: %s.",
490 cf.save_file, strerror(err));
497 gtk_grab_remove(GTK_WIDGET(cap_w));
498 gtk_widget_destroy(GTK_WIDGET(cap_w));
500 if (quit_after_cap) {
501 /* DON'T unlink the save file. Presumably someone wants it. */
505 if ((err = open_cap_file(cf.save_file, &cf)) == 0) {
506 /* Set the read filter to NULL. */
508 err = read_cap_file(&cf);
509 set_menu_sensitivity("/File/Save", TRUE);
510 set_menu_sensitivity("/File/Save As...", FALSE);
515 /* We couldn't even start the capture, so get rid of the capture
517 unlink(cf.save_file); /* silently ignore error */
519 /* Send the error message to our parent, so they can display a
520 dialog box containing it. */
521 int msglen = strlen(errmsg);
523 sprintf(lenbuf, "%u;", msglen);
524 write(1, lenbuf, strlen(lenbuf));
525 write(1, errmsg, msglen);
527 /* Display the dialog box ourselves; there's no parent. */
528 simple_dialog(ESD_TYPE_WARN, NULL, errmsg);
538 pct(gint num, gint denom) {
540 return (float) num * 100.0 / (float) denom;
547 capture_stop_cb(GtkWidget *w, gpointer data) {
548 loop_data *ld = (loop_data *) data;
554 capture_pcap_cb(u_char *user, const struct pcap_pkthdr *phdr,
556 struct wtap_pkthdr whdr;
557 loop_data *ld = (loop_data *) user;
560 if ((++ld->counts.total >= ld->max) && (ld->max > 0))
566 whdr.caplen = phdr->caplen;
567 whdr.len = phdr->len;
568 whdr.pkt_encap = ld->linktype;
570 /* XXX - do something if this fails */
571 wtap_dump(ld->pdh, &whdr, pd, &err);
574 switch (ld->linktype) {
575 case WTAP_ENCAP_ETHERNET:
576 capture_eth(pd, phdr->caplen, &ld->counts);
578 case WTAP_ENCAP_FDDI:
579 case WTAP_ENCAP_FDDI_BITSWAPPED:
580 capture_fddi(pd, phdr->caplen, &ld->counts);
583 capture_tr(pd, phdr->caplen, &ld->counts);
585 case WTAP_ENCAP_NULL:
586 capture_null(pd, phdr->caplen, &ld->counts);
589 capture_ppp(pd, phdr->caplen, &ld->counts);
591 case WTAP_ENCAP_RAW_IP:
592 capture_raw(pd, phdr->caplen, &ld->counts);
594 /* XXX - FreeBSD may append 4-byte ATM pseudo-header to DLT_ATM_RFC1483,
595 with LLC header following; we should implement it at some
600 #endif /* HAVE_LIBPCAP */