summaryrefslogtreecommitdiff
path: root/pcap-linux.c
diff options
context:
space:
mode:
Diffstat (limited to 'pcap-linux.c')
-rw-r--r--pcap-linux.c809
1 files changed, 578 insertions, 231 deletions
diff --git a/pcap-linux.c b/pcap-linux.c
index a35a379c4622..70334b3c860b 100644
--- a/pcap-linux.c
+++ b/pcap-linux.c
@@ -339,9 +339,6 @@ struct pcap_linux {
static int get_if_flags(const char *, bpf_u_int32 *, char *);
static int is_wifi(int, const char *);
static void map_arphrd_to_dlt(pcap_t *, int, int, const char *, int);
-#ifdef HAVE_PF_PACKET_SOCKETS
-static short int map_packet_type_to_sll_type(short int);
-#endif
static int pcap_activate_linux(pcap_t *);
static int activate_old(pcap_t *);
static int activate_new(pcap_t *);
@@ -486,7 +483,7 @@ static int iface_bind_old(int fd, const char *device, char *ebuf);
#ifdef SO_ATTACH_FILTER
static int fix_program(pcap_t *handle, struct sock_fprog *fcode,
int is_mapped);
-static int fix_offset(struct bpf_insn *p);
+static int fix_offset(pcap_t *handle, struct bpf_insn *p);
static int set_kernel_filter(pcap_t *handle, struct sock_fprog *fcode);
static int reset_kernel_filter(pcap_t *handle);
@@ -977,7 +974,7 @@ added:
* Now configure the monitor interface up.
*/
memset(&ifr, 0, sizeof(ifr));
- strlcpy(ifr.ifr_name, handlep->mondevice, sizeof(ifr.ifr_name));
+ pcap_strlcpy(ifr.ifr_name, handlep->mondevice, sizeof(ifr.ifr_name));
if (ioctl(sock_fd, SIOCGIFFLAGS, &ifr) == -1) {
pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
errno, "%s: Can't get flags for %s", device,
@@ -1038,7 +1035,7 @@ is_bonding_device(int fd, const char *device)
ifbond ifb;
memset(&ifr, 0, sizeof ifr);
- strlcpy(ifr.ifr_name, device, sizeof ifr.ifr_name);
+ pcap_strlcpy(ifr.ifr_name, device, sizeof ifr.ifr_name);
memset(&ifb, 0, sizeof ifb);
ifr.ifr_data = (caddr_t)&ifb;
if (ioctl(fd, BOND_INFO_QUERY_IOCTL, &ifr) == 0)
@@ -1127,7 +1124,7 @@ pcap_can_set_rfmon_linux(pcap_t *handle)
/*
* Attempt to get the current mode.
*/
- strlcpy(ireq.ifr_ifrn.ifrn_name, handle->opt.device,
+ pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, handle->opt.device,
sizeof ireq.ifr_ifrn.ifrn_name);
if (ioctl(sock_fd, SIOCGIWMODE, &ireq) != -1) {
/*
@@ -1160,16 +1157,16 @@ static long int
linux_if_drops(const char * if_name)
{
char buffer[512];
- char * bufptr;
- FILE * file;
- int field_to_convert = 3, if_name_sz = strlen(if_name);
+ FILE *file;
+ char *bufptr, *nameptr, *colonptr;
+ int field_to_convert = 3;
long int dropped_pkts = 0;
file = fopen("/proc/net/dev", "r");
if (!file)
return 0;
- while (!dropped_pkts && fgets( buffer, sizeof(buffer), file ))
+ while (fgets(buffer, sizeof(buffer), file) != NULL)
{
/* search for 'bytes' -- if its in there, then
that means we need to grab the fourth field. otherwise
@@ -1180,26 +1177,71 @@ linux_if_drops(const char * if_name)
continue;
}
- /* find iface and make sure it actually matches -- space before the name and : after it */
- if ((bufptr = strstr(buffer, if_name)) &&
- (bufptr == buffer || *(bufptr-1) == ' ') &&
- *(bufptr + if_name_sz) == ':')
+ /*
+ * See whether this line corresponds to this device.
+ * The line should have zero or more leading blanks,
+ * followed by a device name, followed by a colon,
+ * followed by the statistics.
+ */
+ bufptr = buffer;
+ /* Skip leading blanks */
+ while (*bufptr == ' ')
+ bufptr++;
+ nameptr = bufptr;
+ /* Look for the colon */
+ colonptr = strchr(nameptr, ':');
+ if (colonptr == NULL)
+ {
+ /*
+ * Not found; this could, for example, be the
+ * header line.
+ */
+ continue;
+ }
+ /* Null-terminate the interface name. */
+ *colonptr = '\0';
+ if (strcmp(if_name, nameptr) == 0)
{
- bufptr = bufptr + if_name_sz + 1;
+ /*
+ * OK, this line has the statistics for the interface.
+ * Skip past the interface name.
+ */
+ bufptr = colonptr + 1;
/* grab the nth field from it */
- while( --field_to_convert && *bufptr != '\0')
+ while (--field_to_convert && *bufptr != '\0')
{
- while (*bufptr != '\0' && *(bufptr++) == ' ');
- while (*bufptr != '\0' && *(bufptr++) != ' ');
- }
+ /*
+ * This isn't the field we want.
+ * First, skip any leading blanks before
+ * the field.
+ */
+ while (*bufptr == ' ')
+ bufptr++;
- /* get rid of any final spaces */
- while (*bufptr != '\0' && *bufptr == ' ') bufptr++;
+ /*
+ * Now skip the non-blank characters of
+ * the field.
+ */
+ while (*bufptr != '\0' && *bufptr != ' ')
+ bufptr++;
+ }
- if (*bufptr != '\0')
- dropped_pkts = strtol(bufptr, NULL, 10);
+ if (field_to_convert == 0)
+ {
+ /*
+ * We've found the field we want.
+ * Skip any leading blanks before it.
+ */
+ while (*bufptr == ' ')
+ bufptr++;
+ /*
+ * Now extract the value, if we have one.
+ */
+ if (*bufptr != '\0')
+ dropped_pkts = strtol(bufptr, NULL, 10);
+ }
break;
}
}
@@ -1250,7 +1292,7 @@ static void pcap_cleanup_linux( pcap_t *handle )
* in 2.0[.x] kernels.
*/
memset(&ifr, 0, sizeof(ifr));
- strlcpy(ifr.ifr_name, handlep->device,
+ pcap_strlcpy(ifr.ifr_name, handlep->device,
sizeof(ifr.ifr_name));
if (ioctl(handle->fd, SIOCGIFFLAGS, &ifr) == -1) {
fprintf(stderr,
@@ -1314,7 +1356,7 @@ static void pcap_cleanup_linux( pcap_t *handle )
*/
oldflags = 0;
memset(&ifr, 0, sizeof(ifr));
- strlcpy(ifr.ifr_name, handlep->device,
+ pcap_strlcpy(ifr.ifr_name, handlep->device,
sizeof(ifr.ifr_name));
if (ioctl(handle->fd, SIOCGIFFLAGS, &ifr) != -1) {
if (ifr.ifr_flags & IFF_UP) {
@@ -1328,7 +1370,7 @@ static void pcap_cleanup_linux( pcap_t *handle )
/*
* Now restore the mode.
*/
- strlcpy(ireq.ifr_ifrn.ifrn_name, handlep->device,
+ pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, handlep->device,
sizeof ireq.ifr_ifrn.ifrn_name);
ireq.u.mode = handlep->oldmode;
if (ioctl(handle->fd, SIOCSIWMODE, &ireq) == -1) {
@@ -1538,7 +1580,8 @@ pcap_activate_linux(pcap_t *handle)
if (handlep->device == NULL) {
pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
errno, "strdup");
- return PCAP_ERROR;
+ status = PCAP_ERROR;
+ goto fail;
}
/* copy timeout value */
@@ -1602,11 +1645,10 @@ pcap_activate_linux(pcap_t *handle)
/*
* We failed to set up to use it, or the kernel
* supports it, but we failed to enable it.
- * ret has been set to the error status to
+ * status has been set to the error status to
* return and, if it's PCAP_ERROR, handle->errbuf
* contains the error message.
*/
- status = ret;
goto fail;
}
}
@@ -1750,7 +1792,6 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata)
int offset;
#ifdef HAVE_PF_PACKET_SOCKETS
struct sockaddr_ll from;
- struct sll_header *hdrp;
#else
struct sockaddr from;
#endif
@@ -1774,9 +1815,12 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata)
* If this is a cooked device, leave extra room for a
* fake packet header.
*/
- if (handlep->cooked)
- offset = SLL_HDR_LEN;
- else
+ if (handlep->cooked) {
+ if (handle->linktype == DLT_LINUX_SLL2)
+ offset = SLL2_HDR_LEN;
+ else
+ offset = SLL_HDR_LEN;
+ } else
offset = 0;
#else
/*
@@ -1906,17 +1950,37 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata)
* Add the length of the fake header to the length
* of packet data we read.
*/
- packet_len += SLL_HDR_LEN;
-
- hdrp = (struct sll_header *)bp;
- hdrp->sll_pkttype = map_packet_type_to_sll_type(from.sll_pkttype);
- hdrp->sll_hatype = htons(from.sll_hatype);
- hdrp->sll_halen = htons(from.sll_halen);
- memcpy(hdrp->sll_addr, from.sll_addr,
- (from.sll_halen > SLL_ADDRLEN) ?
- SLL_ADDRLEN :
- from.sll_halen);
- hdrp->sll_protocol = from.sll_protocol;
+ if (handle->linktype == DLT_LINUX_SLL2) {
+ struct sll2_header *hdrp;
+
+ packet_len += SLL2_HDR_LEN;
+
+ hdrp = (struct sll2_header *)bp;
+ hdrp->sll2_protocol = from.sll_protocol;
+ hdrp->sll2_reserved_mbz = 0;
+ hdrp->sll2_if_index = htonl(from.sll_ifindex);
+ hdrp->sll2_hatype = htons(from.sll_hatype);
+ hdrp->sll2_pkttype = from.sll_pkttype;
+ hdrp->sll2_halen = from.sll_halen;
+ memcpy(hdrp->sll2_addr, from.sll_addr,
+ (from.sll_halen > SLL_ADDRLEN) ?
+ SLL_ADDRLEN :
+ from.sll_halen);
+ } else {
+ struct sll_header *hdrp;
+
+ packet_len += SLL_HDR_LEN;
+
+ hdrp = (struct sll_header *)bp;
+ hdrp->sll_pkttype = htons(from.sll_pkttype);
+ hdrp->sll_hatype = htons(from.sll_hatype);
+ hdrp->sll_halen = htons(from.sll_halen);
+ memcpy(hdrp->sll_addr, from.sll_addr,
+ (from.sll_halen > SLL_ADDRLEN) ?
+ SLL_ADDRLEN :
+ from.sll_halen);
+ hdrp->sll_protocol = from.sll_protocol;
+ }
}
/*
@@ -2121,7 +2185,7 @@ pcap_inject_linux(pcap_t *handle, const void *buf, size_t size)
/*
* We don't support sending on the "any" device.
*/
- strlcpy(handle->errbuf,
+ pcap_strlcpy(handle->errbuf,
"Sending packets isn't supported on the \"any\" device",
PCAP_ERRBUF_SIZE);
return (-1);
@@ -2129,13 +2193,13 @@ pcap_inject_linux(pcap_t *handle, const void *buf, size_t size)
if (handlep->cooked) {
/*
- * We don't support sending on the "any" device.
+ * We don't support sending on cooked-mode sockets.
*
* XXX - how do you send on a bound cooked-mode
* socket?
* Is a "sendto()" required there?
*/
- strlcpy(handle->errbuf,
+ pcap_strlcpy(handle->errbuf,
"Sending packets isn't supported in cooked mode",
PCAP_ERRBUF_SIZE);
return (-1);
@@ -2349,7 +2413,7 @@ add_linux_if(pcap_if_list_t *devlistp, const char *ifname, int fd, char *errbuf)
/*
* Get the flags for this interface.
*/
- strlcpy(ifrflags.ifr_name, name, sizeof(ifrflags.ifr_name));
+ pcap_strlcpy(ifrflags.ifr_name, name, sizeof(ifrflags.ifr_name));
if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifrflags) < 0) {
if (errno == ENXIO || errno == ENODEV)
return (0); /* device doesn't actually exist - ignore it */
@@ -2708,7 +2772,7 @@ get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf)
#ifdef ETHTOOL_GLINK
memset(&ifr, 0, sizeof(ifr));
- strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+ pcap_strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
info.cmd = ETHTOOL_GLINK;
ifr.ifr_data = (caddr_t)&info;
if (ioctl(sock, SIOCETHTOOL, &ifr) == -1) {
@@ -2836,7 +2900,7 @@ pcap_setfilter_linux_common(pcap_t *handle, struct bpf_program *filter,
if (!handle)
return -1;
if (!filter) {
- strlcpy(handle->errbuf, "setfilter: No filter specified",
+ pcap_strlcpy(handle->errbuf, "setfilter: No filter specified",
PCAP_ERRBUF_SIZE);
return -1;
}
@@ -3025,41 +3089,6 @@ pcap_setdirection_linux(pcap_t *handle, pcap_direction_t d)
return -1;
}
-#ifdef HAVE_PF_PACKET_SOCKETS
-/*
- * Map the PACKET_ value to a LINUX_SLL_ value; we
- * want the same numerical value to be used in
- * the link-layer header even if the numerical values
- * for the PACKET_ #defines change, so that programs
- * that look at the packet type field will always be
- * able to handle DLT_LINUX_SLL captures.
- */
-static short int
-map_packet_type_to_sll_type(short int sll_pkttype)
-{
- switch (sll_pkttype) {
-
- case PACKET_HOST:
- return htons(LINUX_SLL_HOST);
-
- case PACKET_BROADCAST:
- return htons(LINUX_SLL_BROADCAST);
-
- case PACKET_MULTICAST:
- return htons(LINUX_SLL_MULTICAST);
-
- case PACKET_OTHERHOST:
- return htons(LINUX_SLL_OTHERHOST);
-
- case PACKET_OUTGOING:
- return htons(LINUX_SLL_OUTGOING);
-
- default:
- return -1;
- }
-}
-#endif
-
static int
is_wifi(int sock_fd
#ifndef IW_MODE_MONITOR
@@ -3558,6 +3587,45 @@ static void map_arphrd_to_dlt(pcap_t *handle, int sock_fd, int arptype,
/* ===== Functions to interface to the newer kernels ================== */
+#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
+/*
+ * 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
+
/*
* Try to open a packet socket using the new kernel PF_PACKET interface.
* Returns 1 on success, 0 on an error that means the new interface isn't
@@ -3573,7 +3641,7 @@ activate_new(pcap_t *handle)
const char *device = handle->opt.device;
int is_any_device = (strcmp(device, "any") == 0);
int protocol = pcap_protocol(handle);
- int sock_fd = -1, arptype;
+ int sock_fd = -1, arptype, ret;
#ifdef HAVE_PACKET_AUXDATA
int val;
#endif
@@ -3602,21 +3670,21 @@ activate_new(pcap_t *handle)
*/
return 0;
}
-
- pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
- errno, "socket");
if (errno == EPERM || errno == EACCES) {
/*
* You don't have permission to open the
* socket.
*/
- return PCAP_ERROR_PERM_DENIED;
+ ret = PCAP_ERROR_PERM_DENIED;
} else {
/*
* Other error.
*/
- return PCAP_ERROR;
+ ret = PCAP_ERROR;
}
+ pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
+ errno, "socket");
+ return ret;
}
/* It seems the kernel supports the new interface. */
@@ -3711,20 +3779,21 @@ activate_new(pcap_t *handle)
}
sock_fd = socket(PF_PACKET, SOCK_DGRAM, protocol);
if (sock_fd == -1) {
- pcap_fmt_errmsg_for_errno(handle->errbuf,
- PCAP_ERRBUF_SIZE, errno, "socket");
if (errno == EPERM || errno == EACCES) {
/*
* You don't have permission to
* open the socket.
*/
- return PCAP_ERROR_PERM_DENIED;
+ ret = PCAP_ERROR_PERM_DENIED;
} else {
/*
* Other error.
*/
- return PCAP_ERROR;
+ ret = PCAP_ERROR;
}
+ pcap_fmt_errmsg_for_errno(handle->errbuf,
+ PCAP_ERRBUF_SIZE, errno, "socket");
+ return ret;
}
handlep->cooked = 1;
@@ -3737,6 +3806,7 @@ activate_new(pcap_t *handle)
free(handle->dlt_list);
handle->dlt_list = NULL;
handle->dlt_count = 0;
+ set_dlt_list_cooked(handle, sock_fd);
}
if (handle->linktype == -1) {
@@ -3797,6 +3867,9 @@ activate_new(pcap_t *handle)
*/
handlep->cooked = 1;
handle->linktype = DLT_LINUX_SLL;
+ handle->dlt_list = NULL;
+ handle->dlt_count = 0;
+ set_dlt_list_cooked(handle, sock_fd);
/*
* We're not bound to a device.
@@ -3837,7 +3910,7 @@ activate_new(pcap_t *handle)
if (setsockopt(sock_fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP,
&mr, sizeof(mr)) == -1) {
pcap_fmt_errmsg_for_errno(handle->errbuf,
- PCAP_ERRBUF_SIZE, errno, "setsockopt");
+ PCAP_ERRBUF_SIZE, errno, "setsockopt (PACKET_ADD_MEMBERSHIP)");
close(sock_fd);
return PCAP_ERROR;
}
@@ -3850,7 +3923,7 @@ activate_new(pcap_t *handle)
if (setsockopt(sock_fd, SOL_PACKET, PACKET_AUXDATA, &val,
sizeof(val)) == -1 && errno != ENOPROTOOPT) {
pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
- errno, "setsockopt");
+ errno, "setsockopt (PACKET_AUXDATA)");
close(sock_fd);
return PCAP_ERROR;
}
@@ -3870,10 +3943,14 @@ activate_new(pcap_t *handle)
* large enough to hold a "cooked mode" header plus
* 1 byte of packet data (so we don't pass a byte
* count of 0 to "recvfrom()").
+ * XXX - we don't know whether this will be DLT_LINUX_SLL
+ * or DLT_LINUX_SLL2, so make sure it's big enough for
+ * a DLT_LINUX_SLL2 "cooked mode" header; a snapshot length
+ * that small is silly anyway.
*/
if (handlep->cooked) {
- if (handle->snapshot < SLL_HDR_LEN + 1)
- handle->snapshot = SLL_HDR_LEN + 1;
+ if (handle->snapshot < SLL2_HDR_LEN + 1)
+ handle->snapshot = SLL2_HDR_LEN + 1;
}
handle->bufsize = handle->snapshot;
@@ -3940,7 +4017,7 @@ activate_new(pcap_t *handle)
return 1;
#else /* HAVE_PF_PACKET_SOCKETS */
- strlcpy(ebuf,
+ pcap_strlcpy(ebuf,
"New packet capturing interface not supported by build "
"environment", PCAP_ERRBUF_SIZE);
return 0;
@@ -4070,10 +4147,20 @@ init_tpacket(pcap_t *handle, int version, const char *version_str)
/*
* Probe whether kernel supports the specified TPACKET version;
* this also gets the length of the header for that version.
+ *
+ * This socket option was introduced in 2.6.27, which was
+ * also the first release with TPACKET_V2 support.
*/
if (getsockopt(handle->fd, SOL_PACKET, PACKET_HDRLEN, &val, &len) < 0) {
- if (errno == ENOPROTOOPT || errno == EINVAL)
+ if (errno == ENOPROTOOPT || errno == EINVAL) {
+ /*
+ * ENOPROTOOPT means the kernel is too old to
+ * support PACKET_HDRLEN at all, which means
+ * it either doesn't support TPACKET at all
+ * or supports only TPACKET_V1.
+ */
return 1; /* no */
+ }
/* Failed to even find out; this is a fatal error. */
pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
@@ -4092,7 +4179,10 @@ init_tpacket(pcap_t *handle, int version, const char *version_str)
}
handlep->tp_version = version;
- /* Reserve space for VLAN tag reconstruction */
+ /*
+ * Reserve space for VLAN tag reconstruction.
+ * This option was also introduced in 2.6.27.
+ */
val = VLAN_TAG_LEN;
if (setsockopt(handle->fd, SOL_PACKET, PACKET_RESERVE, &val,
sizeof(val)) < 0) {
@@ -4203,7 +4293,10 @@ prepare_tpacket_socket(pcap_t *handle)
#endif /* HAVE_TPACKET2 */
/*
- * OK, we're using TPACKET_V1, as that's all the kernel supports.
+ * OK, we're using TPACKET_V1, as either that's all the kernel
+ * supports or it doesn't support TPACKET at all. In the latter
+ * case, create_ring() will fail, and we'll fall back on non-
+ * memory-mapped capture.
*/
handlep->tp_version = TPACKET_V1;
handlep->tp_hdrlen = sizeof(struct tpacket_hdr);
@@ -4359,7 +4452,7 @@ create_ring(pcap_t *handle, int *status)
if (getsockopt(handle->fd, SOL_SOCKET, SO_TYPE, &sk_type,
&len) < 0) {
pcap_fmt_errmsg_for_errno(handle->errbuf,
- PCAP_ERRBUF_SIZE, errno, "getsockopt");
+ PCAP_ERRBUF_SIZE, errno, "getsockopt (SO_TYPE)");
*status = PCAP_ERROR;
return -1;
}
@@ -4374,14 +4467,50 @@ create_ring(pcap_t *handle, int *status)
* as best we can.
*/
pcap_fmt_errmsg_for_errno(handle->errbuf,
- PCAP_ERRBUF_SIZE, errno, "getsockopt");
+ PCAP_ERRBUF_SIZE, errno,
+ "getsockopt (PACKET_RESERVE)");
+ *status = PCAP_ERROR;
+ return -1;
+ }
+ /*
+ * Older kernel, so we can't use PACKET_RESERVE;
+ * this means we can't reserver extra space
+ * for a DLT_LINUX_SLL2 header.
+ */
+ tp_reserve = 0;
+ } else {
+ /*
+ * We can reserve extra space for a DLT_LINUX_SLL2
+ * header. Do so.
+ *
+ * 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.
+ *
+ * XXX - should we use TPACKET_ALIGN(SLL2_HDR_LEN - SLL_HDR_LEN)?
+ */
+ tp_reserve += SLL2_HDR_LEN - SLL_HDR_LEN;
+ len = sizeof(tp_reserve);
+ if (setsockopt(handle->fd, SOL_PACKET, PACKET_RESERVE,
+ &tp_reserve, len) < 0) {
+ pcap_fmt_errmsg_for_errno(handle->errbuf,
+ PCAP_ERRBUF_SIZE, errno,
+ "setsockopt (PACKET_RESERVE)");
*status = PCAP_ERROR;
return -1;
}
- tp_reserve = 0; /* older kernel, reserve not supported */
}
#else
- tp_reserve = 0; /* older kernel, reserve not supported */
+ /*
+ * Build environment for an older kernel, so we can't
+ * use PACKET_RESERVE; this means we can't reserve
+ * extra space for a DLT_LINUX_SLL2 header.
+ */
+ tp_reserve = 0;
#endif
maclen = (sk_type == SOCK_DGRAM) ? 0 : MAX_LINKHEADER_SIZE;
/* XXX: in the kernel maclen is calculated from
@@ -4425,6 +4554,49 @@ create_ring(pcap_t *handle, int *status)
#ifdef HAVE_TPACKET3
case TPACKET_V3:
+ /*
+ * If we have TPACKET_V3, we have PACKET_RESERVE.
+ */
+ len = sizeof(tp_reserve);
+ if (getsockopt(handle->fd, SOL_PACKET, PACKET_RESERVE,
+ &tp_reserve, &len) < 0) {
+ /*
+ * Even ENOPROTOOPT is an error - we wouldn't
+ * be here if the kernel didn't support
+ * TPACKET_V3, which means it supports
+ * PACKET_RESERVE.
+ */
+ pcap_fmt_errmsg_for_errno(handle->errbuf,
+ PCAP_ERRBUF_SIZE, errno,
+ "getsockopt (PACKET_RESERVE)");
+ *status = PCAP_ERROR;
+ return -1;
+ }
+ /*
+ * We can reserve extra space for a DLT_LINUX_SLL2
+ * header. Do so.
+ *
+ * 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.
+ *
+ * XXX - should we use TPACKET_ALIGN(SLL2_HDR_LEN - SLL_HDR_LEN)?
+ */
+ tp_reserve += SLL2_HDR_LEN - SLL_HDR_LEN;
+ len = sizeof(tp_reserve);
+ if (setsockopt(handle->fd, SOL_PACKET, PACKET_RESERVE,
+ &tp_reserve, len) < 0) {
+ pcap_fmt_errmsg_for_errno(handle->errbuf,
+ PCAP_ERRBUF_SIZE, errno,
+ "setsockopt (PACKET_RESERVE)");
+ *status = PCAP_ERROR;
+ return -1;
+ }
+
/* The "frames" for this are actually buffers that
* contain multiple variable-sized frames.
*
@@ -4496,7 +4668,7 @@ create_ring(pcap_t *handle, int *status)
hwconfig.rx_filter = HWTSTAMP_FILTER_ALL;
memset(&ifr, 0, sizeof(ifr));
- strlcpy(ifr.ifr_name, handle->opt.device, sizeof(ifr.ifr_name));
+ pcap_strlcpy(ifr.ifr_name, handle->opt.device, sizeof(ifr.ifr_name));
ifr.ifr_data = (void *)&hwconfig;
if (ioctl(handle->fd, SIOCSHWTSTAMP, &ifr) < 0) {
@@ -4580,7 +4752,26 @@ retry:
#ifdef HAVE_TPACKET3
/* timeout value to retire block - use the configured buffering timeout, or default if <0. */
- req.tp_retire_blk_tov = (handlep->timeout>=0)?handlep->timeout:0;
+ if (handlep->timeout > 0) {
+ /* Use the user specified timeout as the block timeout */
+ req.tp_retire_blk_tov = handlep->timeout;
+ } else if (handlep->timeout == 0) {
+ /*
+ * In pcap, this means "infinite timeout"; TPACKET_V3
+ * doesn't support that, so just set it to UINT_MAX
+ * milliseconds. In the TPACKET_V3 loop, if the
+ * timeout is 0, and we haven't yet seen any packets,
+ * and we block and still don't have any packets, we
+ * keep blocking until we do.
+ */
+ req.tp_retire_blk_tov = UINT_MAX;
+ } else {
+ /*
+ * XXX - this is not valid; use 0, meaning "have the
+ * kernel pick a default", for now.
+ */
+ req.tp_retire_blk_tov = 0;
+ }
/* private data not used */
req.tp_sizeof_priv = 0;
/* Rx ring - feature request bits - none (rxhash will not be filled) */
@@ -4908,13 +5099,27 @@ static int pcap_handle_packet_mmap(
struct sockaddr_ll *sll;
struct pcap_pkthdr pcaphdr;
unsigned int snaplen = tp_snaplen;
+ struct utsname utsname;
/* perform sanity check on internal offset. */
if (tp_mac + tp_snaplen > handle->bufsize) {
- pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "corrupted frame on kernel ring mac "
- "offset %u + caplen %u > frame len %d",
- tp_mac, tp_snaplen, handle->bufsize);
+ /*
+ * Report some system information as a debugging aid.
+ */
+ if (uname(&utsname) != -1) {
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "corrupted frame on kernel ring mac "
+ "offset %u + caplen %u > frame len %d "
+ "(kernel %.32s version %s, machine %.16s)",
+ tp_mac, tp_snaplen, handle->bufsize,
+ utsname.release, utsname.version,
+ utsname.machine);
+ } else {
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "corrupted frame on kernel ring mac "
+ "offset %u + caplen %u > frame len %d",
+ tp_mac, tp_snaplen, handle->bufsize);
+ }
return -1;
}
@@ -4932,44 +5137,85 @@ static int pcap_handle_packet_mmap(
/* if required build in place the sll header*/
sll = (void *)frame + TPACKET_ALIGN(handlep->tp_hdrlen);
if (handlep->cooked) {
- struct sll_header *hdrp;
+ if (handle->linktype == DLT_LINUX_SLL2) {
+ struct sll2_header *hdrp;
- /*
- * The kernel should have left us with enough
- * space for an sll header; back up the packet
- * data pointer into that space, as that'll be
- * the beginning of the packet we pass to the
- * callback.
- */
- bp -= SLL_HDR_LEN;
+ /*
+ * The kernel should have left us with enough
+ * space for an sll header; back up the packet
+ * data pointer into that space, as that'll be
+ * the beginning of the packet we pass to the
+ * callback.
+ */
+ bp -= SLL2_HDR_LEN;
- /*
- * Let's make sure that's past the end of
- * the tpacket header, i.e. >=
- * ((u_char *)thdr + TPACKET_HDRLEN), so we
- * don't step on the header when we construct
- * the sll header.
- */
- if (bp < (u_char *)frame +
- TPACKET_ALIGN(handlep->tp_hdrlen) +
- sizeof(struct sockaddr_ll)) {
- pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "cooked-mode frame doesn't have room for sll header");
- return -1;
- }
+ /*
+ * Let's make sure that's past the end of
+ * the tpacket header, i.e. >=
+ * ((u_char *)thdr + TPACKET_HDRLEN), so we
+ * don't step on the header when we construct
+ * the sll header.
+ */
+ if (bp < (u_char *)frame +
+ TPACKET_ALIGN(handlep->tp_hdrlen) +
+ sizeof(struct sockaddr_ll)) {
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "cooked-mode frame doesn't have room for sll header");
+ return -1;
+ }
- /*
- * OK, that worked; construct the sll header.
- */
- hdrp = (struct sll_header *)bp;
- hdrp->sll_pkttype = map_packet_type_to_sll_type(
- sll->sll_pkttype);
- hdrp->sll_hatype = htons(sll->sll_hatype);
- hdrp->sll_halen = htons(sll->sll_halen);
- memcpy(hdrp->sll_addr, sll->sll_addr, SLL_ADDRLEN);
- hdrp->sll_protocol = sll->sll_protocol;
+ /*
+ * OK, that worked; construct the sll header.
+ */
+ hdrp = (struct sll2_header *)bp;
+ hdrp->sll2_protocol = sll->sll_protocol;
+ hdrp->sll2_reserved_mbz = 0;
+ hdrp->sll2_if_index = htonl(sll->sll_ifindex);
+ hdrp->sll2_hatype = htons(sll->sll_hatype);
+ hdrp->sll2_pkttype = sll->sll_pkttype;
+ hdrp->sll2_halen = sll->sll_halen;
+ memcpy(hdrp->sll2_addr, sll->sll_addr, SLL_ADDRLEN);
+
+ snaplen += sizeof(struct sll2_header);
+ } else {
+ struct sll_header *hdrp;
+
+ /*
+ * The kernel should have left us with enough
+ * space for an sll header; back up the packet
+ * data pointer into that space, as that'll be
+ * the beginning of the packet we pass to the
+ * callback.
+ */
+ bp -= SLL_HDR_LEN;
+
+ /*
+ * Let's make sure that's past the end of
+ * the tpacket header, i.e. >=
+ * ((u_char *)thdr + TPACKET_HDRLEN), so we
+ * don't step on the header when we construct
+ * the sll header.
+ */
+ if (bp < (u_char *)frame +
+ TPACKET_ALIGN(handlep->tp_hdrlen) +
+ sizeof(struct sockaddr_ll)) {
+ pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "cooked-mode frame doesn't have room for sll header");
+ return -1;
+ }
- snaplen += sizeof(struct sll_header);
+ /*
+ * OK, that worked; construct the sll header.
+ */
+ hdrp = (struct sll_header *)bp;
+ hdrp->sll_pkttype = htons(sll->sll_pkttype);
+ hdrp->sll_hatype = htons(sll->sll_hatype);
+ hdrp->sll_halen = htons(sll->sll_halen);
+ memcpy(hdrp->sll_addr, sll->sll_addr, SLL_ADDRLEN);
+ hdrp->sll_protocol = sll->sll_protocol;
+
+ snaplen += sizeof(struct sll_header);
+ }
}
if (handlep->filter_in_userland && handle->fcode.bf_insns) {
@@ -4998,8 +5244,13 @@ static int pcap_handle_packet_mmap(
/* if required build in place the sll header*/
if (handlep->cooked) {
/* update packet len */
- pcaphdr.caplen += SLL_HDR_LEN;
- pcaphdr.len += SLL_HDR_LEN;
+ if (handle->linktype == DLT_LINUX_SLL2) {
+ pcaphdr.caplen += SLL2_HDR_LEN;
+ pcaphdr.len += SLL2_HDR_LEN;
+ } else {
+ pcaphdr.caplen += SLL_HDR_LEN;
+ pcaphdr.len += SLL_HDR_LEN;
+ }
}
#if defined(HAVE_TPACKET2) || defined(HAVE_TPACKET3)
@@ -5521,7 +5772,7 @@ iface_get_id(int fd, const char *device, char *ebuf)
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
- strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
+ pcap_strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
if (ioctl(fd, SIOCGIFINDEX, &ifr) == -1) {
pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE,
@@ -5570,7 +5821,7 @@ iface_bind(int fd, int ifindex, char *ebuf, int protocol)
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE,
- errno, "getsockopt");
+ errno, "getsockopt (SO_ERROR)");
return 0;
}
@@ -5602,19 +5853,22 @@ static int
has_wext(int sock_fd, const char *device, char *ebuf)
{
struct iwreq ireq;
+ int ret;
if (is_bonding_device(sock_fd, device))
return 0; /* bonding device, so don't even try */
- strlcpy(ireq.ifr_ifrn.ifrn_name, device,
+ pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device,
sizeof ireq.ifr_ifrn.ifrn_name);
if (ioctl(sock_fd, SIOCGIWNAME, &ireq) >= 0)
return 1; /* yes */
+ if (errno == ENODEV)
+ ret = PCAP_ERROR_NO_SUCH_DEVICE;
+ else
+ ret = 0;
pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, errno,
"%s: SIOCGIWNAME", device);
- if (errno == ENODEV)
- return PCAP_ERROR_NO_SUCH_DEVICE;
- return 0;
+ return ret;
}
/*
@@ -5743,7 +5997,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
* return EOPNOTSUPP.
*/
memset(&ireq, 0, sizeof ireq);
- strlcpy(ireq.ifr_ifrn.ifrn_name, device,
+ pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device,
sizeof ireq.ifr_ifrn.ifrn_name);
ireq.u.data.pointer = (void *)args;
ireq.u.data.length = 0;
@@ -5943,7 +6197,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
/*
* Get the old mode.
*/
- strlcpy(ireq.ifr_ifrn.ifrn_name, device,
+ pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device,
sizeof ireq.ifr_ifrn.ifrn_name);
if (ioctl(sock_fd, SIOCGIWMODE, &ireq) == -1) {
/*
@@ -5999,7 +6253,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
* If it fails, just fall back on SIOCSIWMODE.
*/
memset(&ireq, 0, sizeof ireq);
- strlcpy(ireq.ifr_ifrn.ifrn_name, device,
+ pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device,
sizeof ireq.ifr_ifrn.ifrn_name);
ireq.u.data.length = 1; /* 1 argument */
args[0] = 3; /* request Prism header */
@@ -6031,7 +6285,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
* might get EBUSY.
*/
memset(&ifr, 0, sizeof(ifr));
- strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
+ pcap_strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
if (ioctl(sock_fd, SIOCGIFFLAGS, &ifr) == -1) {
pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE,
errno, "%s: Can't get flags", device);
@@ -6052,7 +6306,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
/*
* Then turn monitor mode on.
*/
- strlcpy(ireq.ifr_ifrn.ifrn_name, device,
+ pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device,
sizeof ireq.ifr_ifrn.ifrn_name);
ireq.u.mode = IW_MODE_MONITOR;
if (ioctl(sock_fd, SIOCSIWMODE, &ireq) == -1) {
@@ -6092,7 +6346,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
* Try to select the radiotap header.
*/
memset(&ireq, 0, sizeof ireq);
- strlcpy(ireq.ifr_ifrn.ifrn_name, device,
+ pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device,
sizeof ireq.ifr_ifrn.ifrn_name);
args[0] = 3; /* request radiotap header */
memcpy(ireq.u.name, args, sizeof (int));
@@ -6103,7 +6357,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
* That failed. Try to select the AVS header.
*/
memset(&ireq, 0, sizeof ireq);
- strlcpy(ireq.ifr_ifrn.ifrn_name, device,
+ pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device,
sizeof ireq.ifr_ifrn.ifrn_name);
args[0] = 2; /* request AVS header */
memcpy(ireq.u.name, args, sizeof (int));
@@ -6114,7 +6368,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
* That failed. Try to select the Prism header.
*/
memset(&ireq, 0, sizeof ireq);
- strlcpy(ireq.ifr_ifrn.ifrn_name, device,
+ pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device,
sizeof ireq.ifr_ifrn.ifrn_name);
args[0] = 1; /* request Prism header */
memcpy(ireq.u.name, args, sizeof (int));
@@ -6132,7 +6386,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
* Select the Prism header.
*/
memset(&ireq, 0, sizeof ireq);
- strlcpy(ireq.ifr_ifrn.ifrn_name, device,
+ pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device,
sizeof ireq.ifr_ifrn.ifrn_name);
args[0] = 3; /* request Prism header */
memcpy(ireq.u.name, args, sizeof (int));
@@ -6144,7 +6398,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
* Get the current channel.
*/
memset(&ireq, 0, sizeof ireq);
- strlcpy(ireq.ifr_ifrn.ifrn_name, device,
+ pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device,
sizeof ireq.ifr_ifrn.ifrn_name);
if (ioctl(sock_fd, SIOCGIWFREQ, &ireq) == -1) {
pcap_fmt_errmsg_for_errno(handle->errbuf,
@@ -6158,7 +6412,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
* current value.
*/
memset(&ireq, 0, sizeof ireq);
- strlcpy(ireq.ifr_ifrn.ifrn_name, device,
+ pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device,
sizeof ireq.ifr_ifrn.ifrn_name);
args[0] = 1; /* request Prism header */
args[1] = channel; /* set channel */
@@ -6172,7 +6426,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
* Prism header.
*/
memset(&ireq, 0, sizeof ireq);
- strlcpy(ireq.ifr_ifrn.ifrn_name, device,
+ pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device,
sizeof ireq.ifr_ifrn.ifrn_name);
args[0] = 0; /* disallow transmitting */
memcpy(ireq.u.name, args, sizeof (int));
@@ -6184,7 +6438,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
* Force the Prism header.
*/
memset(&ireq, 0, sizeof ireq);
- strlcpy(ireq.ifr_ifrn.ifrn_name, device,
+ pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device,
sizeof ireq.ifr_ifrn.ifrn_name);
args[0] = 1; /* request Prism header */
memcpy(ireq.u.name, args, sizeof (int));
@@ -6196,7 +6450,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
* Force the Prism header.
*/
memset(&ireq, 0, sizeof ireq);
- strlcpy(ireq.ifr_ifrn.ifrn_name, device,
+ pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device,
sizeof ireq.ifr_ifrn.ifrn_name);
ireq.u.data.length = 1; /* 1 argument */
ireq.u.data.pointer = "1";
@@ -6209,7 +6463,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device)
* Force the Prism header.
*/
memset(&ireq, 0, sizeof ireq);
- strlcpy(ireq.ifr_ifrn.ifrn_name, device,
+ pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device,
sizeof ireq.ifr_ifrn.ifrn_name);
args[0] = 1; /* request Prism header */
memcpy(ireq.u.name, args, sizeof (int));
@@ -6357,7 +6611,7 @@ iface_ethtool_get_ts_info(const char *device, pcap_t *handle, char *ebuf)
}
memset(&ifr, 0, sizeof(ifr));
- strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
+ pcap_strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
memset(&info, 0, sizeof(info));
info.cmd = ETHTOOL_GET_TS_INFO;
ifr.ifr_data = (caddr_t)&info;
@@ -6471,21 +6725,35 @@ iface_ethtool_get_ts_info(const char *device, pcap_t *handle, char *ebuf _U_)
* if SIOCETHTOOL isn't defined, or we don't have any #defines for any
* of the types of offloading, there's nothing we can do to check, so
* we just say "no, we don't".
+ *
+ * We treat EOPNOTSUPP, EINVAL and, if eperm_ok is true, EPERM as
+ * indications that the operation isn't supported. We do EPERM
+ * weirdly because the SIOCETHTOOL code in later kernels 1) doesn't
+ * support ETHTOOL_GUFO, 2) also doesn't include it in the list
+ * of ethtool operations that don't require CAP_NET_ADMIN privileges,
+ * and 3) does the "is this permitted" check before doing the "is
+ * this even supported" check, so it fails with "this is not permitted"
+ * rather than "this is not even supported". To work around this
+ * annoyance, we only treat EPERM as an error for the first feature,
+ * and assume that they all do the same permission checks, so if the
+ * first one is allowed all the others are allowed if supported.
*/
#if defined(SIOCETHTOOL) && (defined(ETHTOOL_GTSO) || defined(ETHTOOL_GUFO) || defined(ETHTOOL_GGSO) || defined(ETHTOOL_GFLAGS) || defined(ETHTOOL_GGRO))
static int
-iface_ethtool_flag_ioctl(pcap_t *handle, int cmd, const char *cmdname)
+iface_ethtool_flag_ioctl(pcap_t *handle, int cmd, const char *cmdname,
+ int eperm_ok)
{
struct ifreq ifr;
struct ethtool_value eval;
memset(&ifr, 0, sizeof(ifr));
- strlcpy(ifr.ifr_name, handle->opt.device, sizeof(ifr.ifr_name));
+ pcap_strlcpy(ifr.ifr_name, handle->opt.device, sizeof(ifr.ifr_name));
eval.cmd = cmd;
eval.data = 0;
ifr.ifr_data = (caddr_t)&eval;
if (ioctl(handle->fd, SIOCETHTOOL, &ifr) == -1) {
- if (errno == EOPNOTSUPP || errno == EINVAL) {
+ if (errno == EOPNOTSUPP || errno == EINVAL ||
+ (errno == EPERM && eperm_ok)) {
/*
* OK, let's just return 0, which, in our
* case, either means "no, what we're asking
@@ -6502,34 +6770,41 @@ iface_ethtool_flag_ioctl(pcap_t *handle, int cmd, const char *cmdname)
return eval.data;
}
+/*
+ * XXX - it's annoying that we have to check for offloading at all, but,
+ * given that we have to, it's still annoying that we have to check for
+ * particular types of offloading, especially that shiny new types of
+ * offloading may be added - and, worse, may not be checkable with
+ * a particular ETHTOOL_ operation; ETHTOOL_GFEATURES would, in
+ * theory, give those to you, but the actual flags being used are
+ * opaque (defined in a non-uapi header), and there doesn't seem to
+ * be any obvious way to ask the kernel what all the offloading flags
+ * are - at best, you can ask for a set of strings(!) to get *names*
+ * for various flags. (That whole mechanism appears to have been
+ * designed for the sole purpose of letting ethtool report flags
+ * by name and set flags by name, with the names having no semantics
+ * ethtool understands.)
+ */
static int
iface_get_offload(pcap_t *handle)
{
int ret;
#ifdef ETHTOOL_GTSO
- ret = iface_ethtool_flag_ioctl(handle, ETHTOOL_GTSO, "ETHTOOL_GTSO");
+ ret = iface_ethtool_flag_ioctl(handle, ETHTOOL_GTSO, "ETHTOOL_GTSO", 0);
if (ret == -1)
return -1;
if (ret)
return 1; /* TCP segmentation offloading on */
#endif
-#ifdef ETHTOOL_GUFO
- ret = iface_ethtool_flag_ioctl(handle, ETHTOOL_GUFO, "ETHTOOL_GUFO");
- if (ret == -1)
- return -1;
- if (ret)
- return 1; /* UDP fragmentation offloading on */
-#endif
-
#ifdef ETHTOOL_GGSO
/*
* XXX - will this cause large unsegmented packets to be
* handed to PF_PACKET sockets on transmission? If not,
* this need not be checked.
*/
- ret = iface_ethtool_flag_ioctl(handle, ETHTOOL_GGSO, "ETHTOOL_GGSO");
+ ret = iface_ethtool_flag_ioctl(handle, ETHTOOL_GGSO, "ETHTOOL_GGSO", 0);
if (ret == -1)
return -1;
if (ret)
@@ -6537,7 +6812,7 @@ iface_get_offload(pcap_t *handle)
#endif
#ifdef ETHTOOL_GFLAGS
- ret = iface_ethtool_flag_ioctl(handle, ETHTOOL_GFLAGS, "ETHTOOL_GFLAGS");
+ ret = iface_ethtool_flag_ioctl(handle, ETHTOOL_GFLAGS, "ETHTOOL_GFLAGS", 0);
if (ret == -1)
return -1;
if (ret & ETH_FLAG_LRO)
@@ -6550,13 +6825,27 @@ iface_get_offload(pcap_t *handle)
* handed to PF_PACKET sockets on receipt? If not,
* this need not be checked.
*/
- ret = iface_ethtool_flag_ioctl(handle, ETHTOOL_GGRO, "ETHTOOL_GGRO");
+ ret = iface_ethtool_flag_ioctl(handle, ETHTOOL_GGRO, "ETHTOOL_GGRO", 0);
if (ret == -1)
return -1;
if (ret)
return 1; /* generic (large) receive offloading on */
#endif
+#ifdef ETHTOOL_GUFO
+ /*
+ * Do this one last, as support for it was removed in later
+ * kernels, and it fails with EPERM on those kernels rather
+ * than with EOPNOTSUPP (see explanation in comment for
+ * iface_ethtool_flag_ioctl()).
+ */
+ ret = iface_ethtool_flag_ioctl(handle, ETHTOOL_GUFO, "ETHTOOL_GUFO", 1);
+ if (ret == -1)
+ return -1;
+ if (ret)
+ return 1; /* UDP fragmentation offloading on */
+#endif
+
return 0;
}
#else /* SIOCETHTOOL */
@@ -6592,8 +6881,17 @@ activate_old(pcap_t *handle)
struct utsname utsname;
int mtu;
- /* Open the socket */
+ /*
+ * PF_INET/SOCK_PACKET sockets must be bound to a device, so we
+ * can't support the "any" device.
+ */
+ if (strcmp(device, "any") == 0) {
+ pcap_strlcpy(handle->errbuf, "pcap_activate: The \"any\" device isn't supported on 2.0[.x]-kernel systems",
+ PCAP_ERRBUF_SIZE);
+ return PCAP_ERROR;
+ }
+ /* Open the socket */
handle->fd = socket(PF_INET, SOCK_PACKET, htons(ETH_P_ALL));
if (handle->fd == -1) {
err = errno;
@@ -6620,12 +6918,6 @@ activate_old(pcap_t *handle)
handlep->cooked = 0;
/* Bind to the given device */
-
- if (strcmp(device, "any") == 0) {
- strlcpy(handle->errbuf, "pcap_activate: The \"any\" device isn't supported on 2.0[.x]-kernel systems",
- PCAP_ERRBUF_SIZE);
- return PCAP_ERROR;
- }
if (iface_bind_old(handle->fd, device, handle->errbuf) == -1)
return PCAP_ERROR;
@@ -6651,7 +6943,7 @@ activate_old(pcap_t *handle)
if (handle->opt.promisc) {
memset(&ifr, 0, sizeof(ifr));
- strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
+ pcap_strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
if (ioctl(handle->fd, SIOCGIFFLAGS, &ifr) == -1) {
pcap_fmt_errmsg_for_errno(handle->errbuf,
PCAP_ERRBUF_SIZE, errno, "SIOCGIFFLAGS");
@@ -6757,6 +7049,17 @@ activate_old(pcap_t *handle)
*
* We can safely pass "recvfrom()" a byte count
* based on the snapshot length.
+ *
+ * XXX - this "should not happen", as 2.2[.x]
+ * kernels all have PF_PACKET sockets, and there's
+ * no configuration option to disable them without
+ * disabling SOCK_PACKET sockets, because
+ * SOCK_PACKET sockets are implemented in the same
+ * source file, net/packet/af_packet.c. There *is*
+ * an option to disable SOCK_PACKET sockets so that
+ * you only have PF_PACKET sockets, and the kernel
+ * will log warning messages for code that uses
+ * "obsolete (PF_INET,SOCK_PACKET)".
*/
handle->bufsize = (u_int)handle->snapshot;
}
@@ -6788,7 +7091,7 @@ iface_bind_old(int fd, const char *device, char *ebuf)
socklen_t errlen = sizeof(err);
memset(&saddr, 0, sizeof(saddr));
- strlcpy(saddr.sa_data, device, sizeof(saddr.sa_data));
+ pcap_strlcpy(saddr.sa_data, device, sizeof(saddr.sa_data));
if (bind(fd, &saddr, sizeof(saddr)) == -1) {
pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE,
errno, "bind");
@@ -6799,7 +7102,7 @@ iface_bind_old(int fd, const char *device, char *ebuf)
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE,
- errno, "getsockopt");
+ errno, "getsockopt (SO_ERROR)");
return -1;
}
@@ -6827,7 +7130,7 @@ iface_get_mtu(int fd, const char *device, char *ebuf)
return BIGGER_THAN_ALL_MTUS;
memset(&ifr, 0, sizeof(ifr));
- strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
+ pcap_strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
if (ioctl(fd, SIOCGIFMTU, &ifr) == -1) {
pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE,
@@ -6845,20 +7148,22 @@ static int
iface_get_arptype(int fd, const char *device, char *ebuf)
{
struct ifreq ifr;
+ int ret;
memset(&ifr, 0, sizeof(ifr));
- strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
+ pcap_strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
if (ioctl(fd, SIOCGIFHWADDR, &ifr) == -1) {
- pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE,
- errno, "SIOCGIFHWADDR");
if (errno == ENODEV) {
/*
* No such device.
*/
- return PCAP_ERROR_NO_SUCH_DEVICE;
- }
- return PCAP_ERROR;
+ ret = PCAP_ERROR_NO_SUCH_DEVICE;
+ } else
+ ret = PCAP_ERROR;
+ pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE,
+ errno, "SIOCGIFHWADDR");
+ return ret;
}
return ifr.ifr_hwaddr.sa_family;
@@ -6950,7 +7255,7 @@ fix_program(pcap_t *handle, struct sock_fprog *fcode, int is_mmapped)
* Yes, so we need to fix this
* instruction.
*/
- if (fix_offset(p) < 0) {
+ if (fix_offset(handle, p) < 0) {
/*
* We failed to do so.
* Return 0, so our caller
@@ -6968,38 +7273,80 @@ fix_program(pcap_t *handle, struct sock_fprog *fcode, int is_mmapped)
}
static int
-fix_offset(struct bpf_insn *p)
+fix_offset(pcap_t *handle, struct bpf_insn *p)
{
/*
- * What's the offset?
+ * Existing references to auxiliary data shouldn't be adjusted.
+ *
+ * Note that SKF_AD_OFF is negative, but p->k is unsigned, so
+ * we use >= and cast SKF_AD_OFF to unsigned.
*/
- if (p->k >= SLL_HDR_LEN) {
- /*
- * It's within the link-layer payload; that starts at an
- * offset of 0, as far as the kernel packet filter is
- * concerned, so subtract the length of the link-layer
- * header.
- */
- p->k -= SLL_HDR_LEN;
- } else if (p->k == 0) {
- /*
- * It's the packet type field; map it to the special magic
- * kernel offset for that field.
- */
- p->k = SKF_AD_OFF + SKF_AD_PKTTYPE;
- } else if (p->k == 14) {
+ if (p->k >= (bpf_u_int32)SKF_AD_OFF)
+ return 0;
+ if (handle->linktype == DLT_LINUX_SLL2) {
/*
- * It's the protocol field; map it to the special magic
- * kernel offset for that field.
+ * What's the offset?
*/
- p->k = SKF_AD_OFF + SKF_AD_PROTOCOL;
- } else if ((bpf_int32)(p->k) > 0) {
+ if (p->k >= SLL2_HDR_LEN) {
+ /*
+ * It's within the link-layer payload; that starts
+ * at an offset of 0, as far as the kernel packet
+ * filter is concerned, so subtract the length of
+ * the link-layer header.
+ */
+ p->k -= SLL2_HDR_LEN;
+ } else if (p->k == 0) {
+ /*
+ * It's the protocol field; map it to the
+ * special magic kernel offset for that field.
+ */
+ p->k = SKF_AD_OFF + SKF_AD_PROTOCOL;
+ } else if (p->k == 10) {
+ /*
+ * It's the packet type field; map it to the
+ * special magic kernel offset for that field.
+ */
+ p->k = SKF_AD_OFF + SKF_AD_PKTTYPE;
+ } else if ((bpf_int32)(p->k) > 0) {
+ /*
+ * It's within the header, but it's not one of
+ * those fields; we can't do that in the kernel,
+ * so punt to userland.
+ */
+ return -1;
+ }
+ } else {
/*
- * It's within the header, but it's not one of those
- * fields; we can't do that in the kernel, so punt
- * to userland.
+ * What's the offset?
*/
- return -1;
+ if (p->k >= SLL_HDR_LEN) {
+ /*
+ * It's within the link-layer payload; that starts
+ * at an offset of 0, as far as the kernel packet
+ * filter is concerned, so subtract the length of
+ * the link-layer header.
+ */
+ p->k -= SLL_HDR_LEN;
+ } else if (p->k == 0) {
+ /*
+ * It's the packet type field; map it to the
+ * special magic kernel offset for that field.
+ */
+ p->k = SKF_AD_OFF + SKF_AD_PKTTYPE;
+ } else if (p->k == 14) {
+ /*
+ * It's the protocol field; map it to the
+ * special magic kernel offset for that field.
+ */
+ p->k = SKF_AD_OFF + SKF_AD_PROTOCOL;
+ } else if ((bpf_int32)(p->k) > 0) {
+ /*
+ * It's within the header, but it's not one of
+ * those fields; we can't do that in the kernel,
+ * so punt to userland.
+ */
+ return -1;
+ }
}
return 0;
}
@@ -7159,7 +7506,7 @@ reset_kernel_filter(pcap_t *handle)
#endif
int
-pcap_set_protocol(pcap_t *p, int protocol)
+pcap_set_protocol_linux(pcap_t *p, int protocol)
{
if (pcap_check_activated(p))
return (PCAP_ERROR_ACTIVATED);