summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorguy <guy>2002-02-22 09:20:59 +0000
committerguy <guy>2002-02-22 09:20:59 +0000
commit7ef4ce5bb24ccc2a0b85e1d623432e180c70c7de (patch)
treef1cd48f7f131c8937208e831b661eef0aa0cf21e
parentf31c72a26096a0ffa9a47feef25c727a22dc472d (diff)
downloadlibpcap-7ef4ce5bb24ccc2a0b85e1d623432e180c70c7de.tar.gz
Bug fixes from Graeme Hewson <ghewson@cix.compulink.co.uk>:
1. During termination processing set up by atexit() under a 2.0.x kernel, if a socket had been previously closed and the handle freed due to an error, pcap_close_all() and pcap_close_linux() would nevertheless try to work with these structures and then crash. pcap_close_linux() is now called directly when necessary during error processing. 2. atexit() could get called more than once because the did_atexit flag wasn't being set. 3. If iface_get_arptype() returns an error because the ioctl() call failed (probably due to "no such device"), live_open_new() now returns a fatal error to pcap_open_live() and the call to live_open_old() is short-circuited. 4. Applications using libpcap would appear to listen on an interface that was down. a. iface_bind() and iface_bind_old() now check for pending errors after bind(). In turn, pcap_open_live() now returns an error status if there was a pending error after bind(). b. After draining the socket, set_kernel_filter() now checks to see if the error was the expected EAGAIN and returns a fatal error to pcap_setfilter() if not. In turn, pcap_setfilter() now returns an error status if there was a network error. 5. pcap_setfilter() was putting an error message into errbuf after a failed call to install_bpf_program(). This was unnecessary since install_bpf_program() puts its own error message into errbuf.
-rw-r--r--CREDITS1
-rw-r--r--pcap-linux.c109
2 files changed, 93 insertions, 17 deletions
diff --git a/CREDITS b/CREDITS
index a604760a..60dfc579 100644
--- a/CREDITS
+++ b/CREDITS
@@ -20,6 +20,7 @@ Additional people who have contributed patches:
Chris Pepper <pepper@mail.reppep.com>
Darren Reed <darrenr@reed.wattle.id.au>
Franz Schaefer <schaefer@mond.at>
+ Graeme Hewson <ghewson@cix.compulink.co.uk>
Greg Troxel <gdt@ir.bbn.com>
Hyung Sik Yoon <hsyn@kr.ibm.com>
Igor Khristophorov <igor@atdot.org>
diff --git a/pcap-linux.c b/pcap-linux.c
index d7b9d9c4..8bbf44bf 100644
--- a/pcap-linux.c
+++ b/pcap-linux.c
@@ -26,7 +26,7 @@
*/
#ifndef lint
static const char rcsid[] =
- "@(#) $Header: /tcpdump/master/libpcap/pcap-linux.c,v 1.73.2.3 2002-02-10 00:06:04 guy Exp $ (LBL)";
+ "@(#) $Header: /tcpdump/master/libpcap/pcap-linux.c,v 1.73.2.4 2002-02-22 09:21:01 guy Exp $ (LBL)";
#endif
/*
@@ -213,6 +213,8 @@ pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf)
{
pcap_t *handle;
int mtu;
+ int err;
+ int live_open_ok = 0;
struct utsname utsname;
/* Allocate a handle for this session. */
@@ -264,16 +266,22 @@ pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf)
* trying both methods with the newer method preferred.
*/
- if (! (live_open_new(handle, device, promisc, to_ms, ebuf) ||
- live_open_old(handle, device, promisc, to_ms, ebuf)) )
- {
+ if ((err = live_open_new(handle, device, promisc, to_ms, ebuf)) == 1)
+ live_open_ok = 1;
+ else if (err == 0) {
+ /* Non-fatal error; try old way */
+ if (live_open_old(handle, device, promisc, to_ms, ebuf))
+ live_open_ok = 1;
+ }
+ if (!live_open_ok) {
/*
* Both methods to open the packet socket failed. Tidy
* up and report our failure (ebuf is expected to be
* set by the functions above).
*/
- free(handle->md.device);
+ if (handle->md.device != NULL)
+ free(handle->md.device);
free(handle);
return NULL;
}
@@ -331,8 +339,12 @@ pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf)
*/
mtu = iface_get_mtu(handle->fd, device, ebuf);
if (mtu == -1) {
+ if (handle->md.clear_promisc)
+ /* 2.0.x kernel */
+ pcap_close_linux(handle);
close(handle->fd);
- free(handle->md.device);
+ if (handle->md.device != NULL)
+ free(handle->md.device);
free(handle);
return NULL;
}
@@ -359,8 +371,12 @@ pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf)
if (!handle->buffer) {
snprintf(ebuf, PCAP_ERRBUF_SIZE,
"malloc: %s", pcap_strerror(errno));
+ if (handle->md.clear_promisc)
+ /* 2.0.x kernel */
+ pcap_close_linux(handle);
close(handle->fd);
- free(handle->md.device);
+ if (handle->md.device != NULL)
+ free(handle->md.device);
free(handle);
return NULL;
}
@@ -719,6 +735,7 @@ pcap_setfilter(pcap_t *handle, struct bpf_program *filter)
#ifdef SO_ATTACH_FILTER
struct sock_fprog fcode;
int can_filter_in_kernel;
+ int err = 0;
#endif
if (!handle)
@@ -731,11 +748,9 @@ pcap_setfilter(pcap_t *handle, struct bpf_program *filter)
/* Make our private copy of the filter */
- if (install_bpf_program(handle, filter) < 0) {
- snprintf(handle->errbuf, sizeof(handle->errbuf),
- "malloc: %s", pcap_strerror(errno));
+ if (install_bpf_program(handle, filter) < 0)
+ /* install_bpf_program() filled in errbuf */
return -1;
- }
/*
* Run user level packet filter by default. Will be overriden if
@@ -809,12 +824,12 @@ pcap_setfilter(pcap_t *handle, struct bpf_program *filter)
}
if (can_filter_in_kernel) {
- if (set_kernel_filter(handle, &fcode) == 0)
+ if ((err = set_kernel_filter(handle, &fcode)) == 0)
{
/* Installation succeded - using kernel filter. */
handle->md.use_bpf = 1;
}
- else
+ else if (err == -1) /* Non-fatal error */
{
/*
* Print a warning if we weren't able to install
@@ -845,6 +860,10 @@ pcap_setfilter(pcap_t *handle, struct bpf_program *filter)
*/
if (fcode.filter != NULL)
free(fcode.filter);
+
+ if (err == -2)
+ /* Fatal error */
+ return -1;
#endif /* SO_ATTACH_FILTER */
return 0;
@@ -1068,6 +1087,8 @@ live_open_new(pcap_t *handle, char *device, int promisc,
{
#ifdef HAVE_PF_PACKET_SOCKETS
int sock_fd = -1, device_id, arptype;
+ int err;
+ int fatal_err = 0;
struct packet_mreq mr;
/* One shot loop used for error handling - bail out with break */
@@ -1120,8 +1141,10 @@ live_open_new(pcap_t *handle, char *device, int promisc,
handle->md.cooked = 0;
arptype = iface_get_arptype(sock_fd, device, ebuf);
- if (arptype == -1)
+ if (arptype == -1) {
+ fatal_err = 1;
break;
+ }
map_arphrd_to_dlt(handle, arptype, 1);
if (handle->linktype == -1 ||
handle->linktype == DLT_LINUX_SLL ||
@@ -1172,8 +1195,11 @@ live_open_new(pcap_t *handle, char *device, int promisc,
if (device_id == -1)
break;
- if (iface_bind(sock_fd, device_id, ebuf) == -1)
+ if ((err = iface_bind(sock_fd, device_id, ebuf)) < 0) {
+ if (err == -2)
+ fatal_err = 1;
break;
+ }
} else {
/*
* This is cooked mode.
@@ -1225,7 +1251,11 @@ live_open_new(pcap_t *handle, char *device, int promisc,
if (sock_fd != -1)
close(sock_fd);
- return 0;
+
+ if (fatal_err)
+ return -2;
+ else
+ return 0;
#else
strncpy(ebuf,
"New packet capturing interface not supported by build "
@@ -1263,6 +1293,8 @@ static int
iface_bind(int fd, int ifindex, char *ebuf)
{
struct sockaddr_ll sll;
+ int err;
+ socklen_t errlen = sizeof(err);
memset(&sll, 0, sizeof(sll));
sll.sll_family = AF_PACKET;
@@ -1275,6 +1307,20 @@ iface_bind(int fd, int ifindex, char *ebuf)
return -1;
}
+ /* Any pending errors, e.g., network is down? */
+
+ if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE,
+ "getsockopt: %s", pcap_strerror(errno));
+ return -2;
+ }
+
+ if (err > 0) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE,
+ "bind: %s", pcap_strerror(err));
+ return -2;
+ }
+
return 0;
}
@@ -1380,8 +1426,10 @@ void pcap_close_linux( pcap_t *handle )
}
}
}
+
if (handle->md.device != NULL)
free(handle->md.device);
+ handle->md.device = NULL;
}
/*
@@ -1475,6 +1523,7 @@ live_open_old(pcap_t *handle, char *device, int promisc,
PCAP_ERRBUF_SIZE);
break;
}
+ did_atexit = 1;
}
ifr.ifr_flags |= IFF_PROMISC;
@@ -1509,6 +1558,8 @@ live_open_old(pcap_t *handle, char *device, int promisc,
} while (0);
+ if (handle->md.clear_promisc)
+ pcap_close_linux(handle);
if (sock_fd != -1)
close(sock_fd);
return 0;
@@ -1522,6 +1573,8 @@ static int
iface_bind_old(int fd, const char *device, char *ebuf)
{
struct sockaddr saddr;
+ int err;
+ socklen_t errlen = sizeof(err);
memset(&saddr, 0, sizeof(saddr));
strncpy(saddr.sa_data, device, sizeof(saddr.sa_data));
@@ -1531,6 +1584,20 @@ iface_bind_old(int fd, const char *device, char *ebuf)
return -1;
}
+ /* Any pending errors, e.g., network is down? */
+
+ if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE,
+ "getsockopt: %s", pcap_strerror(errno));
+ return -1;
+ }
+
+ if (err > 0) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE,
+ "bind: %s", pcap_strerror(err));
+ return -1;
+ }
+
return 0;
}
@@ -1756,7 +1823,7 @@ set_kernel_filter(pcap_t *handle, struct sock_fprog *fcode)
/*
* Save the socket's current mode, and put it in
* non-blocking mode; we drain it by reading packets
- * until we get an error (which we assume is a
+ * until we get an error (which is normally a
* "nothing more to be read" error).
*/
save_mode = fcntl(handle->fd, F_GETFL, 0);
@@ -1765,7 +1832,15 @@ set_kernel_filter(pcap_t *handle, struct sock_fprog *fcode)
while (recv(handle->fd, &drain, sizeof drain,
MSG_TRUNC) >= 0)
;
+ save_errno = errno;
fcntl(handle->fd, F_SETFL, save_mode);
+ if (save_errno != EAGAIN) {
+ /* Fatal error */
+ reset_kernel_filter(handle);
+ snprintf(handle->errbuf, sizeof(handle->errbuf),
+ "recv: %s", pcap_strerror(save_errno));
+ return -2;
+ }
}
}