Ethereal-dev: [ethereal-dev] [patch] pipe capture
Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.
From: Olivier Abad <oabad@xxxxxxxxxxxxx>
Date: Tue, 25 Jul 2000 00:37:07 +0200
Here is a first patch to read captures from a pipe. I didn't change anything in the capture window : if you enter a pipe name (e.g. /tmp/fifo) instead of an interface name, this name is passed to the capture() function, which first tries a pcap_open_live() on it, and if it fails, performs a pipe_open_live() I have created two functions : pipe_open_live() to initialize the capture from the pipe, and pipe_dispatch() to read one record from the pipe and write it in the capture file. Reading from stdin seems to work also. You can do something like this : $ wget -O - http://ethereal.zing.org/sample/v6.pcap | ./ethereal -i - -k Please tell me if you find problems. I'll check the code in CVS later. Olivier P.S. : I guess it will work only on Unix platforms. I'll have to put #ifdefs for win32. -- When you have 200 programmers trying to write code for one product, like Win95 or NT, what you get is a multipule personality program. By definition, the real problem is that these programs are psychotic by nature and make people crazy when they use them. -- Joan Brewer on alt.destroy.microsoft
diff -uNr ethereal/capture.c ethereal.pipe/capture.c --- ethereal/capture.c Fri Jul 21 23:19:05 2000 +++ ethereal.pipe/capture.c Mon Jul 24 23:29:54 2000 @@ -99,6 +99,8 @@ #include "simple_dialog.h" #include "prefs.h" #include "globals.h" +#include "wiretap/libpcap.h" +#include "wiretap/wtap-int.h" #include "packet-clip.h" #include "packet-eth.h" @@ -134,6 +136,9 @@ gint max; gint linktype; gint sync_packets; + gboolean from_pipe; /* TRUE if we are capturing data from a pipe */ + gboolean modified; /* TRUE if data in the pipe uses modified pcap headers */ + gboolean byte_swapped; /* TRUE if data in the pipe is byte swapped */ packet_counts counts; wtap_dumper *pdh; } loop_data; @@ -707,6 +712,204 @@ */ #define CAP_READ_TIMEOUT 250 +static void +adjust_header(loop_data *ld, struct pcap_hdr *hdr, struct pcaprec_hdr *rechdr) +{ + if (ld->byte_swapped) { + /* Byte-swap the record header fields. */ + rechdr->ts_sec = BSWAP32(rechdr->ts_sec); + rechdr->ts_usec = BSWAP32(rechdr->ts_usec); + rechdr->incl_len = BSWAP32(rechdr->incl_len); + rechdr->orig_len = BSWAP32(rechdr->orig_len); + } + if (hdr->version_major == 2 && + (hdr->version_minor < 3 || + (hdr->version_minor == 3 && rechdr->incl_len > rechdr->orig_len))) { + guint32 temp; + + temp = rechdr->orig_len; + rechdr->orig_len = rechdr->incl_len; + rechdr->incl_len = temp; + } +} + +/* mimic pcap_open_live() for pipe captures */ +static int +pipe_open_live(char *pipename, struct pcap_hdr *hdr, loop_data *ld, char *ebuf) +{ + struct stat pipe_stat; + int fd; + guint32 magic; + int bytes_read, b; + + if (strcmp(pipename, "-") == 0) fd = 0; /* read from stdin */ + else if (stat(pipename, &pipe_stat) == 0 && S_ISFIFO(pipe_stat.st_mode)) { + if ((fd = open(pipename, O_RDONLY)) == -1) return -1; + } else return -1; + + ld->from_pipe = TRUE; + /* read the pcap header */ + if (read(fd, &magic, sizeof magic) != sizeof magic) { + close(fd); + return -1; + } + + switch (magic) { + case PCAP_MAGIC: + /* Host that wrote it has our byte order. */ + ld->byte_swapped = FALSE; + ld->modified = FALSE; + break; + case PCAP_MODIFIED_MAGIC: + /* Host that wrote it has our byte order, but was running + a program using the patched "libpcap". */ + ld->byte_swapped = FALSE; + ld->modified = TRUE; + break; + case PCAP_SWAPPED_MAGIC: + /* Host that wrote it has a byte order opposite to ours. */ + ld->byte_swapped = TRUE; + ld->modified = FALSE; + break; + case PCAP_SWAPPED_MODIFIED_MAGIC: + /* Host that wrote it out has a byte order opposite to + ours, and was running a program using the patched + "libpcap". */ + ld->byte_swapped = TRUE; + ld->modified = TRUE; + break; + default: + /* Not a "libpcap" type we know about. */ + close(fd); + return -1; + } + + bytes_read = read(fd, hdr, sizeof(struct pcap_hdr)); + if (bytes_read <= 0) { + close(fd); + return -1; + } + while (bytes_read < sizeof(struct pcap_hdr)) + { + b = read(fd, ((char *)&hdr)+bytes_read, sizeof(struct pcap_hdr) - bytes_read); + if (b <= 0) { + close(fd); + return -1; + } + bytes_read += b; + } + if (ld->byte_swapped) { + /* Byte-swap the header fields about which we care. */ + hdr->version_major = BSWAP16(hdr->version_major); + hdr->version_minor = BSWAP16(hdr->version_minor); + hdr->snaplen = BSWAP32(hdr->snaplen); + hdr->network = BSWAP32(hdr->network); + } + if (hdr->version_major < 2) { + close(fd); + return -1; + } + + return fd; +} + +static int +pipe_dispatch(int fd, loop_data *ld, struct pcap_hdr *hdr) +{ + struct wtap_pkthdr whdr; + struct pcaprec_modified_hdr rechdr; + int bytes_to_read, bytes_read, b; + u_char pd[WTAP_MAX_PACKET_SIZE]; + int err; + + bytes_to_read = ld->modified ? sizeof rechdr : sizeof rechdr.hdr; + bytes_read = read(fd, &rechdr, bytes_to_read); + if (bytes_read <= 0) { + close(fd); + ld->go = FALSE; + fprintf(stderr, "record header read error : %d, errno = %d\n", bytes_read, errno); + return 0; + } + while (bytes_read < bytes_to_read) + { + b = read(fd, ((char *)&rechdr)+bytes_read, bytes_to_read - bytes_read); + if (b <= 0) { + close(fd); + ld->go = FALSE; + fprintf(stderr, "record header read error : %d, errno = %d\n", bytes_read, errno); + return 0; + } + bytes_read += b; + } + adjust_header(ld, hdr, &rechdr.hdr); + if (rechdr.hdr.incl_len > WTAP_MAX_PACKET_SIZE) { + close(fd); + ld->go = FALSE; + fprintf(stderr, "invalid record length : %d\n", rechdr.hdr.incl_len); + return 0; + } + bytes_read = read(fd, pd, rechdr.hdr.incl_len); + if (bytes_read <= 0) { + close(fd); + ld->go = FALSE; + fprintf(stderr, "record read error : %d, errno = %d\n", bytes_read, errno); + return 0; + } + while (bytes_read < rechdr.hdr.incl_len) + { + b = read(fd, pd+bytes_read, rechdr.hdr.incl_len - bytes_read); + if (b <= 0) { + close(fd); + ld->go = FALSE; + fprintf(stderr, "record read error : %d, errno = %d\n", bytes_read, errno); + return 0; + } + bytes_read += b; + } + whdr.ts.tv_sec = rechdr.hdr.ts_sec; + whdr.ts.tv_usec = rechdr.hdr.ts_usec; + whdr.caplen = rechdr.hdr.incl_len; + whdr.len = rechdr.hdr.orig_len; + whdr.pkt_encap = ld->linktype; + wtap_dump(ld->pdh, &whdr, NULL, pd, &err); + + /* Set the initial payload to the packet length, and the initial + captured payload to the capture length (other protocols may + reduce them if their headers say they're less). */ + pi.len = whdr.len; + pi.captured_len = whdr.caplen; + + switch (ld->linktype) { + case WTAP_ENCAP_ETHERNET: + capture_eth(pd, 0, &ld->counts); + break; + case WTAP_ENCAP_FDDI: + case WTAP_ENCAP_FDDI_BITSWAPPED: + capture_fddi(pd, &ld->counts); + break; + case WTAP_ENCAP_TR: + capture_tr(pd, 0, &ld->counts); + break; + case WTAP_ENCAP_NULL: + capture_null(pd, &ld->counts); + break; + case WTAP_ENCAP_PPP: + capture_ppp(pd, 0, &ld->counts); + break; + case WTAP_ENCAP_RAW_IP: + capture_raw(pd, &ld->counts); + break; + case WTAP_ENCAP_LINUX_ATM_CLIP: + capture_clip(pd, &ld->counts); + break; + /* XXX - FreeBSD may append 4-byte ATM pseudo-header to DLT_ATM_RFC1483, + with LLC header following; we should implement it at some + point. */ + } + + return 1; +} + /* Do the low-level work of a capture. Returns TRUE if it succeeds, FALSE otherwise. */ int @@ -724,12 +927,14 @@ #ifdef linux fd_set set1; struct timeval timeout; - int pcap_fd; + int pcap_fd = 0; #endif #ifdef _WIN32 WORD wVersionRequested; WSADATA wsaData; #endif + int pipe_fd = -1; + struct pcap_hdr hdr; /* Initialize Windows Socket if we are in a WIN32 OS This needs to be done before querying the interface for network/netmask */ @@ -748,6 +953,7 @@ ld.counts.total = 0; ld.max = cfile.count; ld.linktype = WTAP_ENCAP_UNKNOWN; + ld.from_pipe = FALSE; ld.sync_packets = 0; ld.counts.sctp = 0; ld.counts.tcp = 0; @@ -765,22 +971,28 @@ pch = pcap_open_live(cfile.iface, cfile.snap, 1, CAP_READ_TIMEOUT, err_str); if (pch == NULL) { - /* Well, we couldn't start the capture. - If this is a child process that does the capturing in sync - mode or fork mode, it shouldn't do any UI stuff until we pop up the - capture-progress window, and, since we couldn't start the - capture, we haven't popped it up. */ - if (!capture_child) { - while (gtk_events_pending()) gtk_main_iteration(); - } - snprintf(errmsg, sizeof errmsg, - "The capture session could not be initiated (%s).\n" - "Please check to make sure you have sufficient permissions, and that\n" - "you have the proper interface specified.", err_str); - goto error; + /* try to open cfile.iface as a pipe */ + pipe_fd = pipe_open_live(cfile.iface, &hdr, &ld, err_str); + + if (pipe_fd == -1) { + /* Well, we couldn't start the capture. + If this is a child process that does the capturing in sync + mode or fork mode, it shouldn't do any UI stuff until we pop up the + capture-progress window, and, since we couldn't start the + capture, we haven't popped it up. */ + if (!capture_child) { + while (gtk_events_pending()) gtk_main_iteration(); + } + snprintf(errmsg, sizeof errmsg, + "The capture session could not be initiated (%s).\n" + "Please check to make sure you have sufficient permissions, and that\n" + "you have the proper interface or pipe specified.", err_str); + goto error; + } } - if (cfile.cfilter) { + /* capture filters only work on real interfaces */ + if (cfile.cfilter && !ld.from_pipe) { /* A capture filter was specified; set it up. */ if (pcap_lookupnet (cfile.iface, &netnum, &netmask, err_str) < 0) { snprintf(errmsg, sizeof errmsg, @@ -800,14 +1012,15 @@ } /* Set up to write to the capture file. */ - ld.linktype = wtap_pcap_encap_to_wtap_encap(pcap_datalink(pch)); + ld.linktype = wtap_pcap_encap_to_wtap_encap(ld.from_pipe ? hdr.network + : pcap_datalink(pch)); if (ld.linktype == WTAP_ENCAP_UNKNOWN) { strcpy(errmsg, "The network you're capturing from is of a type" " that Ethereal doesn't support."); goto error; } ld.pdh = wtap_dump_fdopen(cfile.save_file_fd, WTAP_FILE_PCAP, - ld.linktype, pcap_snapshot(pch), &err); + ld.linktype, ld.from_pipe ? hdr.snaplen : pcap_snapshot(pch), &err); if (ld.pdh == NULL) { /* We couldn't set up to write to the capture file. */ @@ -929,41 +1142,60 @@ upd_time = time(NULL); #ifdef linux - pcap_fd = pcap_fileno(pch); + if (!ld.from_pipe) pcap_fd = pcap_fileno(pch); #endif while (ld.go) { while (gtk_events_pending()) gtk_main_iteration(); + + if (ld.from_pipe) { + FD_ZERO(&set1); + FD_SET(pipe_fd, &set1); + timeout.tv_sec = 0; + timeout.tv_usec = CAP_READ_TIMEOUT*1000; + if (select(pipe_fd+1, &set1, NULL, NULL, &timeout) != 0) { + /* + * "select()" says we can read from the pipe without blocking; go for + * it. We are not sure we can read a whole record, but at least the + * begninning of one. pipe_dispatch() will block reading the whole + * record. + */ + inpkts = pipe_dispatch(pipe_fd, &ld, &hdr); + } else + inpkts = 0; + } + else { #ifdef linux - /* - * Sigh. The semantics of the read timeout argument to - * "pcap_open_live()" aren't particularly well specified by - * the "pcap" man page - at least with the BSD BPF code, the - * intent appears to be, at least in part, a way of cutting - * down the number of reads done on a capture, by blocking - * until the buffer fills or a timer expires - and the Linux - * libpcap doesn't actually support it, so we can't use it - * to break out of the "pcap_dispatch()" every 1/4 of a second - * or so. - * - * Thus, on Linux, we do a "select()" on the file descriptor for the - * capture, with a timeout of CAP_READ_TIMEOUT milliseconds, or - * CAP_READ_TIMEOUT*1000 microseconds. - */ - FD_ZERO(&set1); - FD_SET(pcap_fd, &set1); - timeout.tv_sec = 0; - timeout.tv_usec = CAP_READ_TIMEOUT*1000; - if (select(pcap_fd+1, &set1, NULL, NULL, &timeout) != 0) { /* - * "select()" says we can read from it without blocking; go for - * it. + * Sigh. The semantics of the read timeout argument to + * "pcap_open_live()" aren't particularly well specified by + * the "pcap" man page - at least with the BSD BPF code, the + * intent appears to be, at least in part, a way of cutting + * down the number of reads done on a capture, by blocking + * until the buffer fills or a timer expires - and the Linux + * libpcap doesn't actually support it, so we can't use it + * to break out of the "pcap_dispatch()" every 1/4 of a second + * or so. + * + * Thus, on Linux, we do a "select()" on the file descriptor for the + * capture, with a timeout of CAP_READ_TIMEOUT milliseconds, or + * CAP_READ_TIMEOUT*1000 microseconds. */ - inpkts = pcap_dispatch(pch, 1, capture_pcap_cb, (u_char *) &ld); - } else - inpkts = 0; + FD_ZERO(&set1); + FD_SET(pcap_fd, &set1); + timeout.tv_sec = 0; + timeout.tv_usec = CAP_READ_TIMEOUT*1000; + if (select(pcap_fd+1, &set1, NULL, NULL, &timeout) != 0) { + /* + * "select()" says we can read from it without blocking; go for + * it. + */ + inpkts = pcap_dispatch(pch, 1, capture_pcap_cb, (u_char *) &ld); + } else + inpkts = 0; #else - inpkts = pcap_dispatch(pch, 1, capture_pcap_cb, (u_char *) &ld); + inpkts = pcap_dispatch(pch, 1, capture_pcap_cb, (u_char *) &ld); #endif + } if (inpkts > 0) ld.sync_packets += inpkts; /* Only update once a second so as not to overload slow displays */ @@ -1057,7 +1289,7 @@ break; } } - pcap_close(pch); + if (!ld.from_pipe) pcap_close(pch); gtk_grab_remove(GTK_WIDGET(cap_w)); gtk_widget_destroy(GTK_WIDGET(cap_w)); diff -uNr ethereal/wiretap/libpcap.c ethereal.pipe/wiretap/libpcap.c --- ethereal/wiretap/libpcap.c Sat May 20 01:06:53 2000 +++ ethereal.pipe/wiretap/libpcap.c Sat Jul 22 15:03:23 2000 @@ -33,58 +33,10 @@ /* See source to the "libpcap" library for information on the "libpcap" file format. */ -/* Magic numbers in "libpcap" files. - - "libpcap" file records are written in the byte order of the host that - writes them, and the reader is expected to fix this up. - - PCAP_MAGIC is the magic number, in host byte order; PCAP_SWAPPED_MAGIC - is a byte-swapped version of that. - - PCAP_MODIFIED_MAGIC is for Alexey Kuznetsov's modified "libpcap" - format, as generated on Linux systems that have a "libpcap" with - his patches, at - - http://ftp.sunet.se/pub/os/Linux/ip-routing/lbl-tools/ - - applied; PCAP_SWAPPED_MODIFIED_MAGIC is the byte-swapped version. */ -#define PCAP_MAGIC 0xa1b2c3d4 -#define PCAP_SWAPPED_MAGIC 0xd4c3b2a1 -#define PCAP_MODIFIED_MAGIC 0xa1b2cd34 -#define PCAP_SWAPPED_MODIFIED_MAGIC 0x34cdb2a1 - /* On some systems, the FDDI MAC addresses are bit-swapped. */ #if !defined(ultrix) && !defined(__alpha) && !defined(__bsdi__) #define BIT_SWAPPED_MAC_ADDRS #endif - -/* "libpcap" file header (minus magic number). */ -struct pcap_hdr { - guint16 version_major; /* major version number */ - guint16 version_minor; /* minor version number */ - gint32 thiszone; /* GMT to local correction */ - guint32 sigfigs; /* accuracy of timestamps */ - guint32 snaplen; /* max length of captured packets, in octets */ - guint32 network; /* data link type */ -}; - -/* "libpcap" record header. */ -struct pcaprec_hdr { - guint32 ts_sec; /* timestamp seconds */ - guint32 ts_usec; /* timestamp microseconds */ - guint32 incl_len; /* number of octets of packet saved in file */ - guint32 orig_len; /* actual length of packet */ -}; - -/* "libpcap" record header for Alexey's patched version. */ -struct pcaprec_modified_hdr { - struct pcaprec_hdr hdr; /* the regular header */ - guint32 ifindex; /* index, in *capturing* machine's list of - interfaces, of the interface on which this - packet came in. */ - guint16 protocol; /* Ethernet packet type */ - guint8 pkt_type; /* broadcast/multicast/etc. indication */ -}; static int libpcap_read(wtap *wth, int *err); static void adjust_header(wtap *wth, struct pcaprec_hdr *hdr); diff -uNr ethereal/wiretap/libpcap.h ethereal.pipe/wiretap/libpcap.h --- ethereal/wiretap/libpcap.h Sat Jan 22 07:22:39 2000 +++ ethereal.pipe/wiretap/libpcap.h Sat Jul 22 15:03:25 2000 @@ -21,6 +21,54 @@ * */ +/* Magic numbers in "libpcap" files. + + "libpcap" file records are written in the byte order of the host that + writes them, and the reader is expected to fix this up. + + PCAP_MAGIC is the magic number, in host byte order; PCAP_SWAPPED_MAGIC + is a byte-swapped version of that. + + PCAP_MODIFIED_MAGIC is for Alexey Kuznetsov's modified "libpcap" + format, as generated on Linux systems that have a "libpcap" with + his patches, at + + http://ftp.sunet.se/pub/os/Linux/ip-routing/lbl-tools/ + + applied; PCAP_SWAPPED_MODIFIED_MAGIC is the byte-swapped version. */ +#define PCAP_MAGIC 0xa1b2c3d4 +#define PCAP_SWAPPED_MAGIC 0xd4c3b2a1 +#define PCAP_MODIFIED_MAGIC 0xa1b2cd34 +#define PCAP_SWAPPED_MODIFIED_MAGIC 0x34cdb2a1 + +/* "libpcap" file header (minus magic number). */ +struct pcap_hdr { + guint16 version_major; /* major version number */ + guint16 version_minor; /* minor version number */ + gint32 thiszone; /* GMT to local correction */ + guint32 sigfigs; /* accuracy of timestamps */ + guint32 snaplen; /* max length of captured packets, in octets */ + guint32 network; /* data link type */ +}; + +/* "libpcap" record header. */ +struct pcaprec_hdr { + guint32 ts_sec; /* timestamp seconds */ + guint32 ts_usec; /* timestamp microseconds */ + guint32 incl_len; /* number of octets of packet saved in file */ + guint32 orig_len; /* actual length of packet */ +}; + +/* "libpcap" record header for Alexey's patched version. */ +struct pcaprec_modified_hdr { + struct pcaprec_hdr hdr; /* the regular header */ + guint32 ifindex; /* index, in *capturing* machine's list of + interfaces, of the interface on which this + packet came in. */ + guint16 protocol; /* Ethernet packet type */ + guint8 pkt_type; /* broadcast/multicast/etc. indication */ +}; + int libpcap_open(wtap *wth, int *err); gboolean libpcap_dump_open(wtap_dumper *wdh, int *err); int libpcap_dump_can_write_encap(int filetype, int encap);
- Prev by Date: Re: [ethereal-dev] What does reload do, and does quit save?
- Next by Date: [ethereal-dev] The Edit->Cut, Copy, and Paste menu items greyed out
- Previous by thread: Re: [ethereal-dev] mobile ipv6 dissectors
- Next by thread: [ethereal-dev] The Edit->Cut, Copy, and Paste menu items greyed out
- Index(es):