* pcap-linux.c: Packet capture interface to the Linux kernel
*
*
* License: BSD
*
#error "Libpcap will only work if TPACKET_V2 is supported; you must build for a 2.6.27 or later kernel"
#endif
-/* check for memory mapped access avaibility. We assume every needed
+/* check for memory mapped access availability. We assume every needed
* struct is defined if the macro TPACKET_HDRLEN is defined, because it
* uses many ring related structs and macros */
#ifdef TPACKET3_HDRLEN
*/
static int get_if_flags(const char *, bpf_u_int32 *, char *);
static int is_wifi(const char *);
-static void map_arphrd_to_dlt(pcap_t *, int, const char *, int);
+static int map_arphrd_to_dlt(pcap_t *, int, const char *, int);
static int pcap_activate_linux(pcap_t *);
-static int activate_pf_packet(pcap_t *, int);
-static int setup_mmapped(pcap_t *, int *);
+static int setup_socket(pcap_t *, int);
+static int setup_mmapped(pcap_t *);
static int pcap_can_set_rfmon_linux(pcap_t *);
static int pcap_inject_linux(pcap_t *, const void *, int);
static int pcap_stats_linux(pcap_t *, struct pcap_stat *);
#define RING_GET_CURRENT_FRAME(h) RING_GET_FRAME_AT(h, h->offset)
static void destroy_ring(pcap_t *handle);
-static int create_ring(pcap_t *handle, int *status);
+static int create_ring(pcap_t *handle);
static int prepare_tpacket_socket(pcap_t *handle);
static int pcap_read_linux_mmap_v2(pcap_t *, int, pcap_handler , u_char *);
#ifdef HAVE_TPACKET3
#else
/*
* This is being compiled on a system that lacks TP_STATUS_VLAN_VALID,
- * so we testwith the value it has in the 3.0 and later kernels, so
+ * so we test with the value it has in the 3.0 and later kernels, so
* we can test it if we're running on a system that has it. (If we're
* running on a system that doesn't have it, it won't be set in the
* tp_status field, so the tests of it will always fail; that means
*/
static int iface_get_id(int fd, const char *device, char *ebuf);
static int iface_get_mtu(int fd, const char *device, char *ebuf);
-static int iface_get_arptype(int fd, const char *device, char *ebuf);
-static int iface_bind(int fd, int ifindex, char *ebuf, int protocol);
+static int iface_get_arptype(int fd, const char *device, char *ebuf);
+static int iface_bind(int fd, int ifindex, char *ebuf, int protocol);
static int enter_rfmon_mode(pcap_t *handle, int sock_fd,
const char *device);
-#if defined(HAVE_LINUX_NET_TSTAMP_H) && defined(PACKET_TIMESTAMP)
-static int iface_ethtool_get_ts_info(const char *device, pcap_t *handle,
+static int iface_get_ts_types(const char *device, pcap_t *handle,
char *ebuf);
-#endif
static int iface_get_offload(pcap_t *handle);
static int fix_program(pcap_t *handle, struct sock_fprog *fcode);
handle->activate_op = pcap_activate_linux;
handle->can_set_rfmon_op = pcap_can_set_rfmon_linux;
-#if defined(HAVE_LINUX_NET_TSTAMP_H) && defined(PACKET_TIMESTAMP)
/*
* See what time stamp types we support.
*/
- if (iface_ethtool_get_ts_info(device, handle, ebuf) == -1) {
+ if (iface_get_ts_types(device, handle, ebuf) == -1) {
pcap_close(handle);
return NULL;
}
-#endif
/*
* We claim that we support microsecond and nanosecond time
*
* Compared to /proc/net/dev this avoids counting software drops,
* but may be unimplemented and just return 0.
- * The author has found no straigthforward way to check for support.
+ * The author has found no straightforward way to check for support.
*/
static long long int
linux_get_stat(const char * if_name, const char * stat) {
}
if (handlep->oneshot_buffer != NULL) {
- free(handlep->oneshot_buffer);
+ munmap(handlep->oneshot_buffer, handle->snapshot);
handlep->oneshot_buffer = NULL;
}
handlep->device = NULL;
}
- close(handlep->poll_breakloop_fd);
+ if (handlep->poll_breakloop_fd != -1) {
+ close(handlep->poll_breakloop_fd);
+ handlep->poll_breakloop_fd = -1;
+ }
pcap_cleanup_live_common(handle);
}
uint64_t value = 1;
/* XXX - what if this fails? */
- (void)write(handlep->poll_breakloop_fd, &value, sizeof(value));
+ if (handlep->poll_breakloop_fd != -1)
+ (void)write(handlep->poll_breakloop_fd, &value, sizeof(value));
+}
+
+/*
+ * Set the offset at which to insert VLAN tags.
+ * That should be the offset of the type field.
+ */
+static void
+set_vlan_offset(pcap_t *handle)
+{
+ struct pcap_linux *handlep = handle->priv;
+
+ switch (handle->linktype) {
+
+ case DLT_EN10MB:
+ /*
+ * The type field is after the destination and source
+ * MAC address.
+ */
+ handlep->vlan_offset = 2 * ETH_ALEN;
+ break;
+
+ case DLT_LINUX_SLL:
+ /*
+ * The type field is in the last 2 bytes of the
+ * DLT_LINUX_SLL header.
+ */
+ handlep->vlan_offset = SLL_HDR_LEN - 2;
+ break;
+
+ default:
+ handlep->vlan_offset = -1; /* unknown */
+ break;
+ }
}
/*
const char *device;
int is_any_device;
struct ifreq ifr;
- int status = 0;
- int status2 = 0;
+ int status;
int ret;
device = handle->opt.device;
+ /*
+ * Start out assuming no warnings.
+ */
+ status = 0;
+
/*
* Make sure the name we were handed will fit into the ioctls we
* might perform on the device; if not, return a "No such device"
* we'll be copying it, that won't fit.
*/
if (strlen(device) >= sizeof(ifr.ifr_name)) {
+ /*
+ * There's nothing more to say, so clear the error
+ * message.
+ */
+ handle->errbuf[0] = '\0';
status = PCAP_ERROR_NO_SUCH_DEVICE;
goto fail;
}
* If the "any" device is specified, try to open a SOCK_DGRAM.
* Otherwise, open a SOCK_RAW.
*/
- ret = activate_pf_packet(handle, is_any_device);
+ ret = setup_socket(handle, is_any_device);
if (ret < 0) {
/*
* Fatal error; the return value is the error code,
status = ret;
goto fail;
}
+ if (ret > 0) {
+ /*
+ * We got a warning; return that, as handle->errbuf
+ * might have been overwritten by this warning.
+ */
+ status = ret;
+ }
+
/*
- * Success.
+ * Success (possibly with a warning).
* Try to set up memory-mapped access.
*/
- ret = setup_mmapped(handle, &status);
- if (ret == -1) {
+ ret = setup_mmapped(handle);
+ if (ret < 0) {
/*
* We failed to set up to use it, or the
* kernel supports it, but we failed to
- * enable it. status has been set to the
+ * enable it. The return value is the
* error status to return and, if it's
* PCAP_ERROR, handle->errbuf contains
* the error message.
*/
+ status = ret;
goto fail;
}
+ if (ret > 0) {
+ /*
+ * We got a warning; return that, as handle->errbuf
+ * might have been overwritten by this warning.
+ */
+ status = ret;
+ }
/*
* We succeeded. status has been set to the status to return,
* Now that we have activated the mmap ring, we can
* set the correct protocol.
*/
- if ((status2 = iface_bind(handle->fd, handlep->ifindex,
+ if ((ret = iface_bind(handle->fd, handlep->ifindex,
handle->errbuf, pcap_protocol(handle))) != 0) {
- status = status2;
+ status = ret;
goto fail;
}
pcap_set_datalink_linux(pcap_t *handle, int dlt)
{
handle->linktype = dlt;
+
+ /*
+ * Update the offset at which to insert VLAN tags for the
+ * new link-layer type.
+ */
+ set_vlan_offset(handle);
+
return 0;
}
return -1;
}
-/*
- * Description string for the "any" device.
- */
-static const char any_descr[] = "Pseudo-device that captures on all interfaces";
-
/*
* A PF_PACKET socket can be bound to any network interface.
*/
* capture on them, "why do no interfaces show up?" - when the
* real problem is a permissions problem. Error reports of that
* type require a lot more back-and-forth to debug, as evidenced
- * by many Wireshark bugs/mailing list questions/Q&A questoins.)
+ * by many Wireshark bugs/mailing list questions/Q&A questions.)
*
* So:
*
/*
* Add the "any" device.
- * As it refers to all network devices, not to any particular
- * network device, the notion of "connected" vs. "disconnected"
- * doesn't apply.
*/
- if (add_dev(devlistp, "any",
- PCAP_IF_UP|PCAP_IF_RUNNING|PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE,
- any_descr, errbuf) == NULL)
+ if (pcap_add_any_dev(devlistp, errbuf) == NULL)
return (-1);
return (0);
* to pick some type that works in raw mode, or fail.
*
* Sets the link type to -1 if unable to map the type.
+ *
+ * Returns 0 on success or a PCAP_ERROR_ value on error.
*/
-static void map_arphrd_to_dlt(pcap_t *handle, int arptype,
- const char *device, int cooked_ok)
+static int map_arphrd_to_dlt(pcap_t *handle, int arptype,
+ const char *device, int cooked_ok)
{
static const char cdma_rmnet[] = "cdma_rmnet";
*/
if (strncmp(device, cdma_rmnet, sizeof cdma_rmnet - 1) == 0) {
handle->linktype = DLT_RAW;
- return;
+ return 0;
}
/*
* XXX - are there any other sorts of "fake Ethernet" that
* have ARPHRD_ETHER but that shouldn't offer DLT_DOCSIS as
* a Cisco CMTS won't put traffic onto it or get traffic
- * bridged onto it? ISDN is handled in "activate_pf_packet()",
+ * bridged onto it? ISDN is handled in "setup_socket()",
* as we fall back on cooked mode there, and we use
* is_wifi() to check for 802.11 devices; are there any
* others?
*/
ret = iface_dsa_get_proto_info(device, handle);
if (ret < 0)
- return;
+ return ret;
if (ret == 1) {
/*
* It's not a Wi-Fi device; offer DOCSIS.
*/
handle->dlt_list = (u_int *) malloc(sizeof(u_int) * 2);
- /*
- * If that fails, just leave the list empty.
- */
- if (handle->dlt_list != NULL) {
- handle->dlt_list[0] = DLT_EN10MB;
- handle->dlt_list[1] = DLT_DOCSIS;
- handle->dlt_count = 2;
+ if (handle->dlt_list == NULL) {
+ pcap_fmt_errmsg_for_errno(handle->errbuf,
+ PCAP_ERRBUF_SIZE, errno, "malloc");
+ return (PCAP_ERROR);
}
+ handle->dlt_list[0] = DLT_EN10MB;
+ handle->dlt_list[1] = DLT_DOCSIS;
+ handle->dlt_count = 2;
}
/* FALLTHROUGH */
* IP-over-FC on which somebody wants to capture
* packets.
*/
+ handle->linktype = DLT_FC_2;
handle->dlt_list = (u_int *) malloc(sizeof(u_int) * 3);
- /*
- * If that fails, just leave the list empty.
- */
- if (handle->dlt_list != NULL) {
- handle->dlt_list[0] = DLT_FC_2;
- handle->dlt_list[1] = DLT_FC_2_WITH_FRAME_DELIMS;
- handle->dlt_list[2] = DLT_IP_OVER_FC;
- handle->dlt_count = 3;
+ if (handle->dlt_list == NULL) {
+ pcap_fmt_errmsg_for_errno(handle->errbuf,
+ PCAP_ERRBUF_SIZE, errno, "malloc");
+ return (PCAP_ERROR);
}
- handle->linktype = DLT_FC_2;
+ handle->dlt_list[0] = DLT_FC_2;
+ handle->dlt_list[1] = DLT_FC_2_WITH_FRAME_DELIMS;
+ handle->dlt_list[2] = DLT_IP_OVER_FC;
+ handle->dlt_count = 3;
break;
#ifndef ARPHRD_IRDA
/* We need to save packet direction for IrDA decoding,
* so let's use "Linux-cooked" mode. Jean II
*
- * XXX - this is handled in activate_pf_packet(). */
+ * XXX - this is handled in setup_socket(). */
/* handlep->cooked = 1; */
break;
* pick up the netlink protocol type such as NETLINK_ROUTE,
* NETLINK_GENERIC, NETLINK_FIB_LOOKUP, etc.
*
- * XXX - this is handled in activate_pf_packet().
+ * XXX - this is handled in setup_socket().
*/
/* handlep->cooked = 1; */
break;
handle->linktype = -1;
break;
}
+ return (0);
}
-#ifdef PACKET_RESERVE
-static void
-set_dlt_list_cooked(pcap_t *handle, int sock_fd)
-{
- socklen_t len;
- unsigned int tp_reserve;
-
- /*
- * If we can't do PACKET_RESERVE, we can't reserve extra space
- * for a DLL_LINUX_SLL2 header, so we can't support DLT_LINUX_SLL2.
- */
- len = sizeof(tp_reserve);
- if (getsockopt(sock_fd, SOL_PACKET, PACKET_RESERVE, &tp_reserve,
- &len) == 0) {
- /*
- * Yes, we can do DLL_LINUX_SLL2.
- */
- handle->dlt_list = (u_int *) malloc(sizeof(u_int) * 2);
- /*
- * If that fails, just leave the list empty.
- */
- if (handle->dlt_list != NULL) {
- handle->dlt_list[0] = DLT_LINUX_SLL;
- handle->dlt_list[1] = DLT_LINUX_SLL2;
- handle->dlt_count = 2;
- }
- }
-}
-#else/* PACKET_RESERVE */
-/*
- * The build environment doesn't define PACKET_RESERVE, so we can't reserve
- * extra space for a DLL_LINUX_SLL2 header, so we can't support DLT_LINUX_SLL2.
- */
-static void
-set_dlt_list_cooked(pcap_t *handle _U_, int sock_fd _U_)
-{
-}
-#endif /* PACKET_RESERVE */
-
/*
* Try to set up a PF_PACKET socket.
- * Returns 0 on success and a PCAP_ERROR_ value on failure.
+ * Returns 0 or a PCAP_WARNING_ value on success and a PCAP_ERROR_ value
+ * on failure.
*/
static int
-activate_pf_packet(pcap_t *handle, int is_any_device)
+setup_socket(pcap_t *handle, int is_any_device)
{
struct pcap_linux *handlep = handle->priv;
const char *device = handle->opt.device;
int status = 0;
int sock_fd, arptype;
-#ifdef HAVE_PACKET_AUXDATA
int val;
-#endif
int err = 0;
struct packet_mreq mr;
#if defined(SO_BPF_EXTENSIONS) && defined(SKF_AD_VLAN_TAG_PRESENT)
* socket.
*/
status = PCAP_ERROR_PERM_DENIED;
+ snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "Attempt to create packet socket failed - CAP_NET_RAW may be required");
} else {
/*
* Other error.
close(sock_fd);
return arptype;
}
- map_arphrd_to_dlt(handle, arptype, device, 1);
+ status = map_arphrd_to_dlt(handle, arptype, device, 1);
+ if (status < 0) {
+ close(sock_fd);
+ return status;
+ }
if (handle->linktype == -1 ||
handle->linktype == DLT_LINUX_SLL ||
handle->linktype == DLT_LINUX_IRDA ||
free(handle->dlt_list);
handle->dlt_list = NULL;
handle->dlt_count = 0;
- set_dlt_list_cooked(handle, sock_fd);
}
if (handle->linktype == -1) {
"falling back to cooked "
"socket",
arptype);
+ status = PCAP_WARNING;
}
/*
handle->linktype != DLT_LINUX_LAPD &&
handle->linktype != DLT_NETLINK)
handle->linktype = DLT_LINUX_SLL;
- if (handle->linktype == -1) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "unknown arptype %d, defaulting to cooked mode",
- arptype);
- status = PCAP_WARNING;
- }
}
handlep->ifindex = iface_get_id(sock_fd, device,
/*
* It uses cooked mode.
+ * Support both DLT_LINUX_SLL and DLT_LINUX_SLL2.
*/
handlep->cooked = 1;
handle->linktype = DLT_LINUX_SLL;
- handle->dlt_list = NULL;
- handle->dlt_count = 0;
- set_dlt_list_cooked(handle, sock_fd);
+ handle->dlt_list = (u_int *) malloc(sizeof(u_int) * 2);
+ if (handle->dlt_list == NULL) {
+ pcap_fmt_errmsg_for_errno(handle->errbuf,
+ PCAP_ERRBUF_SIZE, errno, "malloc");
+ return (PCAP_ERROR);
+ }
+ handle->dlt_list[0] = DLT_LINUX_SLL;
+ handle->dlt_list[1] = DLT_LINUX_SLL2;
+ handle->dlt_count = 2;
/*
* We're not bound to a device.
}
}
- /* Enable auxiliary data if supported and reserve room for
- * reconstructing VLAN headers. */
-#ifdef HAVE_PACKET_AUXDATA
+ /*
+ * Enable auxiliary data and reserve room for reconstructing
+ * VLAN headers.
+ *
+ * XXX - is enabling auxiliary data necessary, now that we
+ * only support memory-mapped capture? The kernel's memory-mapped
+ * capture code doesn't seem to check whether auxiliary data
+ * is enabled, it seems to provide it whether it is or not.
+ */
val = 1;
if (setsockopt(sock_fd, SOL_PACKET, PACKET_AUXDATA, &val,
sizeof(val)) == -1 && errno != ENOPROTOOPT) {
return PCAP_ERROR;
}
handle->offset += VLAN_TAG_LEN;
-#endif /* HAVE_PACKET_AUXDATA */
/*
* If we're in cooked mode, make the snapshot length
/*
* Set the offset at which to insert VLAN tags.
- * That should be the offset of the type field.
*/
- switch (handle->linktype) {
-
- case DLT_EN10MB:
- /*
- * The type field is after the destination and source
- * MAC address.
- */
- handlep->vlan_offset = 2 * ETH_ALEN;
- break;
-
- case DLT_LINUX_SLL:
- /*
- * The type field is in the last 2 bytes of the
- * DLT_LINUX_SLL header.
- */
- handlep->vlan_offset = SLL_HDR_LEN - 2;
- break;
-
- default:
- handlep->vlan_offset = -1; /* unknown */
- break;
- }
+ set_vlan_offset(handle);
if (handle->opt.tstamp_precision == PCAP_TSTAMP_PRECISION_NANO) {
int nsec_tstamps = 1;
/*
* Attempt to setup memory-mapped access.
*
- * On success, returns 1, and sets *status to 0 if there are no warnings
- * or to a PCAP_WARNING_ code if there is a warning.
+ * On success, returns 0 if there are no warnings or a PCAP_WARNING_ code
+ * if there is a warning.
*
- * On error, returns -1, and sets *status to the appropriate error code;
- * if that is PCAP_ERROR, sets handle->errbuf to the appropriate message.
+ * On error, returns the appropriate error code; if that is PCAP_ERROR,
+ * sets handle->errbuf to the appropriate message.
*/
static int
-setup_mmapped(pcap_t *handle, int *status)
+setup_mmapped(pcap_t *handle)
{
struct pcap_linux *handlep = handle->priv;
- int ret;
+ int flags = MAP_ANONYMOUS | MAP_PRIVATE;
+ int status;
/*
* Attempt to allocate a buffer to hold the contents of one
* packet, for use by the oneshot callback.
*/
- handlep->oneshot_buffer = malloc(handle->snapshot);
- if (handlep->oneshot_buffer == NULL) {
+#ifdef MAP_32BIT
+ if (pcap_mmap_32bit) flags |= MAP_32BIT;
+#endif
+ handlep->oneshot_buffer = mmap(0, handle->snapshot, PROT_READ | PROT_WRITE, flags, -1, 0);
+ if (handlep->oneshot_buffer == MAP_FAILED) {
pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
errno, "can't allocate oneshot buffer");
- *status = PCAP_ERROR;
- return -1;
+ return PCAP_ERROR;
}
if (handle->opt.buffer_size == 0) {
/* by default request 2M for the ring buffer */
handle->opt.buffer_size = 2*1024*1024;
}
- ret = prepare_tpacket_socket(handle);
- if (ret == -1) {
- free(handlep->oneshot_buffer);
+ status = prepare_tpacket_socket(handle);
+ if (status == -1) {
+ munmap(handlep->oneshot_buffer, handle->snapshot);
handlep->oneshot_buffer = NULL;
- *status = PCAP_ERROR;
- return ret;
+ return PCAP_ERROR;
}
- ret = create_ring(handle, status);
- if (ret == -1) {
+ status = create_ring(handle);
+ if (status < 0) {
/*
* Error attempting to enable memory-mapped capture;
- * fail. create_ring() has set *status.
+ * fail. The return value is the status to return.
*/
- free(handlep->oneshot_buffer);
+ munmap(handlep->oneshot_buffer, handle->snapshot);
handlep->oneshot_buffer = NULL;
- return -1;
+ return status;
}
/*
- * Success. *status has been set either to 0 if there are no
+ * Success. status has been set either to 0 if there are no
* warnings or to a PCAP_WARNING_ value if there is a warning.
*
* handle->offset is used to get the current position into the rx ring.
*/
set_poll_timeout(handlep);
- return 1;
+ return status;
}
/*
/*
* Attempt to set up memory-mapped access.
*
- * On success, returns 1, and sets *status to 0 if there are no warnings
- * or to a PCAP_WARNING_ code if there is a warning.
+ * On success, returns 0 if there are no warnings or to a PCAP_WARNING_ code
+ * if there is a warning.
*
- * On error, returns -1, and sets *status to the appropriate error code;
- * if that is PCAP_ERROR, sets handle->errbuf to the appropriate message.
+ * On error, returns the appropriate error code; if that is PCAP_ERROR,
+ * sets handle->errbuf to the appropriate message.
*/
static int
-create_ring(pcap_t *handle, int *status)
+create_ring(pcap_t *handle)
{
struct pcap_linux *handlep = handle->priv;
unsigned i, j, frames_per_block;
+ int flags = MAP_SHARED;
#ifdef HAVE_TPACKET3
/*
* For sockets using TPACKET_V2, the extra stuff at the end of a
socklen_t len;
unsigned int sk_type, tp_reserve, maclen, tp_hdrlen, netoff, macoff;
unsigned int frame_size;
+ int status;
/*
- * Start out assuming no warnings or errors.
+ * Start out assuming no warnings.
*/
- *status = 0;
+ status = 0;
/*
* Reserve space for VLAN tag reconstruction.
tp_reserve = VLAN_TAG_LEN;
/*
- * If we're using DLT_LINUX_SLL2, reserve space for a
- * DLT_LINUX_SLL2 header.
+ * If we're capturing in cooked mode, reserve space for
+ * a DLT_LINUX_SLL2 header; we don't know yet whether
+ * we'll be using DLT_LINUX_SLL or DLT_LINUX_SLL2, as
+ * that can be changed on an open device, so we reserve
+ * space for the larger of the two.
*
* XXX - we assume that the kernel is still adding
- * 16 bytes of extra space; that happens to
- * correspond to SLL_HDR_LEN (whether intentionally
- * or not - the kernel code has a raw "16" in
- * the expression), so we subtract SLL_HDR_LEN
- * from SLL2_HDR_LEN to get the additional space
- * needed. That also means we don't bother reserving
- * any additional space if we're using DLT_LINUX_SLL.
+ * 16 bytes of extra space, so we subtract 16 from
+ * SLL2_HDR_LEN to get the additional space needed.
+ * (Are they doing that for DLT_LINUX_SLL, the link-
+ * layer header for which is 16 bytes?)
*
- * XXX - should we use TPACKET_ALIGN(SLL2_HDR_LEN - SLL_HDR_LEN)?
+ * XXX - should we use TPACKET_ALIGN(SLL2_HDR_LEN - 16)?
*/
- if (handle->linktype == DLT_LINUX_SLL2)
- tp_reserve += SLL2_HDR_LEN - SLL_HDR_LEN;
+ if (handlep->cooked)
+ tp_reserve += SLL2_HDR_LEN - 16;
/*
* Try to request that amount of reserve space.
* This must be done before creating the ring buffer.
- * If PACKET_RESERVE is supported, creating the ring
- * buffer should be, although if creating the ring
- * buffer fails, the PACKET_RESERVE call has no effect,
- * so falling back on read-from-the-socket capturing
- * won't be affected.
*/
len = sizeof(tp_reserve);
if (setsockopt(handle->fd, SOL_PACKET, PACKET_RESERVE,
&tp_reserve, len) < 0) {
- /*
- * We treat ENOPROTOOPT as an error, as we
- * already determined that we support
- * TPACKET_V2 and later; see above.
- */
pcap_fmt_errmsg_for_errno(handle->errbuf,
PCAP_ERRBUF_SIZE, errno,
"setsockopt (PACKET_RESERVE)");
- *status = PCAP_ERROR;
- return -1;
+ return PCAP_ERROR;
}
switch (handlep->tp_version) {
mtu = iface_get_mtu(handle->fd, handle->opt.device,
handle->errbuf);
- if (mtu == -1) {
- *status = PCAP_ERROR;
- return -1;
- }
+ if (mtu == -1)
+ return PCAP_ERROR;
offload = iface_get_offload(handle);
- if (offload == -1) {
- *status = PCAP_ERROR;
- return -1;
- }
+ if (offload == -1)
+ return PCAP_ERROR;
if (offload)
max_frame_len = MAX(mtu, 65535);
else
&len) < 0) {
pcap_fmt_errmsg_for_errno(handle->errbuf,
PCAP_ERRBUF_SIZE, errno, "getsockopt (SO_TYPE)");
- *status = PCAP_ERROR;
- return -1;
+ return PCAP_ERROR;
}
maclen = (sk_type == SOCK_DGRAM) ? 0 : MAX_LINKHEADER_SIZE;
/* XXX: in the kernel maclen is calculated from
snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
"Internal error: unknown TPACKET_ value %u",
handlep->tp_version);
- *status = PCAP_ERROR;
- return -1;
+ return PCAP_ERROR;
}
/* compute the minimum block size that will handle this frame.
pcap_strlcpy(ifr.ifr_name, handle->opt.device, sizeof(ifr.ifr_name));
ifr.ifr_data = (void *)&hwconfig;
+ /*
+ * This may require CAP_NET_ADMIN.
+ */
if (ioctl(handle->fd, SIOCSHWTSTAMP, &ifr) < 0) {
switch (errno) {
* and, if they can't, shouldn't
* try requesting hardware time stamps.
*/
- *status = PCAP_ERROR_PERM_DENIED;
- return -1;
+ snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "Attempt to set hardware timestamp failed - CAP_NET_ADMIN may be required");
+ return PCAP_ERROR_PERM_DENIED;
case EOPNOTSUPP:
case ERANGE:
* We'll just fall back on the standard
* host time stamps.
*/
- *status = PCAP_WARNING_TSTAMP_TYPE_NOTSUP;
+ status = PCAP_WARNING_TSTAMP_TYPE_NOTSUP;
break;
default:
pcap_fmt_errmsg_for_errno(handle->errbuf,
PCAP_ERRBUF_SIZE, errno,
"SIOCSHWTSTAMP failed");
- *status = PCAP_ERROR;
- return -1;
+ return PCAP_ERROR;
}
} else {
/*
pcap_fmt_errmsg_for_errno(handle->errbuf,
PCAP_ERRBUF_SIZE, errno,
"can't set PACKET_TIMESTAMP");
- *status = PCAP_ERROR;
- return -1;
+ return PCAP_ERROR;
}
}
}
}
pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
errno, "can't create rx ring on packet socket");
- *status = PCAP_ERROR;
- return -1;
+ return PCAP_ERROR;
}
/* memory map the rx ring */
handlep->mmapbuflen = req.tp_block_nr * req.tp_block_size;
- handlep->mmapbuf = mmap(0, handlep->mmapbuflen,
- PROT_READ|PROT_WRITE, MAP_SHARED, handle->fd, 0);
+#ifdef MAP_32BIT
+ if (pcap_mmap_32bit) flags |= MAP_32BIT;
+#endif
+ handlep->mmapbuf = mmap(0, handlep->mmapbuflen, PROT_READ | PROT_WRITE, flags, handle->fd, 0);
if (handlep->mmapbuf == MAP_FAILED) {
pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
errno, "can't mmap rx ring");
/* clear the allocated ring on error*/
destroy_ring(handle);
- *status = PCAP_ERROR;
- return -1;
+ return PCAP_ERROR;
}
/* allocate a ring for each frame header pointer*/
errno, "can't allocate ring of frame headers");
destroy_ring(handle);
- *status = PCAP_ERROR;
- return -1;
+ return PCAP_ERROR;
}
/* fill the header ring with proper frame ptr*/
handle->bufsize = req.tp_frame_size;
handle->offset = 0;
- return 1;
+ return status;
}
/* free all ring related resources*/
*/
handlep->timeout = ~handlep->timeout;
}
+ if (handlep->poll_breakloop_fd != -1) {
+ /* Close the eventfd; we do not need it in nonblock mode. */
+ close(handlep->poll_breakloop_fd);
+ handlep->poll_breakloop_fd = -1;
+ }
} else {
+ if (handlep->poll_breakloop_fd == -1) {
+ /* If we did not have an eventfd, open one now that we are blocking. */
+ if ( ( handlep->poll_breakloop_fd = eventfd(0, EFD_NONBLOCK) ) == -1 ) {
+ int save_errno = errno;
+ snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "Could not open eventfd: %s",
+ strerror(errno));
+ errno = save_errno;
+ return -1;
+ }
+ }
if (handlep->timeout < 0) {
handlep->timeout = ~handlep->timeout;
}
struct ifreq ifr;
int ret;
struct pollfd pollinfo[2];
+ int numpollinfo;
pollinfo[0].fd = handle->fd;
pollinfo[0].events = POLLIN;
- pollinfo[1].fd = handlep->poll_breakloop_fd;
- pollinfo[1].events = POLLIN;
+ if ( handlep->poll_breakloop_fd == -1 ) {
+ numpollinfo = 1;
+ pollinfo[1].revents = 0;
+ /*
+ * We set pollinfo[1].revents to zero, even though
+ * numpollinfo = 1 meaning that poll() doesn't see
+ * pollinfo[1], so that we do not have to add a
+ * conditional of numpollinfo > 1 below when we
+ * test pollinfo[1].revents.
+ */
+ } else {
+ pollinfo[1].fd = handlep->poll_breakloop_fd;
+ pollinfo[1].events = POLLIN;
+ numpollinfo = 2;
+ }
/*
* Keep polling until we either get some packets to read, see
* don't need to poll.
*/
for (;;) {
- /*
+ /*
* Yes, we do this even in non-blocking mode, as it's
* the only way to get error indications from a
* tpacket socket.
if (timeout != 0)
timeout = 1;
}
- ret = poll(pollinfo, 2, timeout);
+ ret = poll(pollinfo, numpollinfo, timeout);
if (ret < 0) {
/*
* Error. If it's not EINTR, report it.
} else {
/*
* Clear CANFD_FDF if it's set (probably
- * again meaning that that field is
+ * again meaning that this field is
* uninitialized junk).
*/
canhdr->fd_flags &= ~CANFD_FDF;
}
}
- /* non-positive values of max_packets are used to require all
- * packets currently available in the ring */
- while ((pkts < max_packets) || PACKET_COUNT_IS_UNLIMITED(max_packets)) {
+ /*
+ * This can conceivably process more than INT_MAX packets,
+ * which would overflow the packet count, causing it either
+ * to look like a negative number, and thus cause us to
+ * return a value that looks like an error, or overflow
+ * back into positive territory, and thus cause us to
+ * return a too-low count.
+ *
+ * Therefore, if the packet count is unlimited, we clip
+ * it at INT_MAX; this routine is not expected to
+ * process packets indefinitely, so that's not an issue.
+ */
+ if (PACKET_COUNT_IS_UNLIMITED(max_packets))
+ max_packets = INT_MAX;
+
+ while (pkts < max_packets) {
/*
* Get the current ring buffer frame, and break if
* it's still owned by the kernel.
return pkts;
}
- /* non-positive values of max_packets are used to require all
- * packets currently available in the ring */
- while ((pkts < max_packets) || PACKET_COUNT_IS_UNLIMITED(max_packets)) {
+ /*
+ * This can conceivably process more than INT_MAX packets,
+ * which would overflow the packet count, causing it either
+ * to look like a negative number, and thus cause us to
+ * return a value that looks like an error, or overflow
+ * back into positive territory, and thus cause us to
+ * return a too-low count.
+ *
+ * Therefore, if the packet count is unlimited, we clip
+ * it at INT_MAX; this routine is not expected to
+ * process packets indefinitely, so that's not an issue.
+ */
+ if (PACKET_COUNT_IS_UNLIMITED(max_packets))
+ max_packets = INT_MAX;
+
+ while (pkts < max_packets) {
int packets_to_read;
if (handlep->current_packet == NULL) {
}
packets_to_read = handlep->packets_left;
- if (!PACKET_COUNT_IS_UNLIMITED(max_packets) &&
- packets_to_read > (max_packets - pkts)) {
+ if (packets_to_read > (max_packets - pkts)) {
/*
- * We've been given a maximum number of packets
- * to process, and there are more packets in
- * this buffer than that. Only process enough
+ * There are more packets in the buffer than
+ * the number of packets we have left to
+ * process to get up to the maximum number
+ * of packets to process. Only process enough
* of them to get us up to that maximum.
*/
packets_to_read = max_packets - pkts;
/* Make our private copy of the filter */
- if (install_bpf_program(handle, filter) < 0)
- /* install_bpf_program() filled in errbuf */
+ if (pcap_install_bpf_program(handle, filter) < 0)
+ /* pcap_install_bpf_program() filled in errbuf */
return -1;
/*
* the filter for a reason other than "this kernel
* isn't configured to support socket filters.
*/
- if (errno != ENOPROTOOPT && errno != EOPNOTSUPP) {
+ if (errno == ENOMEM) {
+ /*
+ * Either a kernel memory allocation
+ * failure occurred, or there's too
+ * much "other/option memory" allocated
+ * for this socket. Suggest that they
+ * increase the "other/option memory"
+ * limit.
+ */
+ fprintf(stderr,
+ "Warning: Couldn't allocate kernel memory for filter: try increasing net.core.optmem_max with sysctl\n");
+ } else if (errno != ENOPROTOOPT && errno != EOPNOTSUPP) {
fprintf(stderr,
"Warning: Kernel filter failed: %s\n",
pcap_strerror(errno));
*/
return PCAP_ERROR_IFACE_NOT_UP;
}
- if (errno == ENODEV)
+ if (errno == ENODEV) {
+ /*
+ * There's nothing more to say, so clear the
+ * error message.
+ */
+ ebuf[0] = '\0';
ret = PCAP_ERROR_NO_SUCH_DEVICE;
- else
+ } else {
ret = PCAP_ERROR;
- pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE,
- errno, "bind");
+ pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE,
+ errno, "bind");
+ }
return ret;
}
return 0;
}
-#ifdef ETHTOOL_GET_TS_INFO
/*
- * Get a list of time stamping capabilities.
+ * Get a list of time stamp types.
*/
+#ifdef ETHTOOL_GET_TS_INFO
static int
-iface_ethtool_get_ts_info(const char *device, pcap_t *handle, char *ebuf)
+iface_get_ts_types(const char *device, pcap_t *handle, char *ebuf)
{
int fd;
struct ifreq ifr;
* report HWTSTAMP_FILTER_ALL but map it to only
* time stamping a few PTP packets. See
* http://marc.info/?l=linux-netdev&m=146318183529571&w=2
+ *
+ * Maybe that got fixed later.
*/
handle->tstamp_type_list = NULL;
return 0;
}
#else /* ETHTOOL_GET_TS_INFO */
static int
-iface_ethtool_get_ts_info(const char *device, pcap_t *handle, char *ebuf)
+iface_get_ts_types(const char *device, pcap_t *handle, char *ebuf)
{
/*
* This doesn't apply to the "any" device; you can't say "turn on
return 0;
}
#endif /* ETHTOOL_GET_TS_INFO */
-
+#else /* defined(HAVE_LINUX_NET_TSTAMP_H) && defined(PACKET_TIMESTAMP) */
+static int
+iface_get_ts_types(const char *device _U_, pcap_t *p _U_, char *ebuf _U_)
+{
+ /*
+ * Nothing to fetch, so it always "succeeds".
+ */
+ return 0;
+}
#endif /* defined(HAVE_LINUX_NET_TSTAMP_H) && defined(PACKET_TIMESTAMP) */
/*
if (errno == ENODEV) {
/*
* No such device.
+ *
+ * There's nothing more to say, so clear
+ * the error message.
*/
ret = PCAP_ERROR_NO_SUCH_DEVICE;
- } else
+ ebuf[0] = '\0';
+ } else {
ret = PCAP_ERROR;
- pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE,
- errno, "SIOCGIFHWADDR");
+ pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE,
+ errno, "SIOCGIFHWADDR");
+ }
return ret;
}