summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuy Harris <guy@alum.mit.edu>2015-02-25 16:56:09 -0800
committerGuy Harris <guy@alum.mit.edu>2015-02-26 21:20:02 -0800
commit987b08971b6fe78a122b4cf5ac041140a93270dc (patch)
tree25c871a49595b811b00da2eb13a48508a675c76a
parent4350c0b26aee5c340014322f63c2d635d64f2d64 (diff)
downloadlibpcap-987b08971b6fe78a122b4cf5ac041140a93270dc.tar.gz
Handle non-x86 K64/U32 combinations.
Have a gauntlet of ISAs where the ISA has both 32-bit and 64-bit flavors and Linux has supported the 64-bit flavor since before TPACKET_V2 as introduced; define ISA_64_BIT to the uname() .machine value for the 64-bit architecture, and compare against that, rather than a hardcoded "x86_64". Clean up white space, expand some comments, and check whether uname() fails.
-rw-r--r--pcap-linux.c88
1 files changed, 74 insertions, 14 deletions
diff --git a/pcap-linux.c b/pcap-linux.c
index 6da8ba74..ed9f58a5 100644
--- a/pcap-linux.c
+++ b/pcap-linux.c
@@ -334,16 +334,25 @@ static int pcap_setdirection_linux(pcap_t *, pcap_direction_t);
static int pcap_set_datalink_linux(pcap_t *, int);
static void pcap_cleanup_linux(pcap_t *);
-// hack for 64bit arch
+/*
+ * This is what the header structure looks like in a 64-bit kernel;
+ * we use this, rather than struct tpacket_hdr, if we're using
+ * TPACKET_V1 in 32-bit code running on a 64-bit kernel.
+ */
struct tpacket_hdr_64 {
- uint64_t tp_status;
- unsigned int tp_len;
- unsigned int tp_snaplen;
- unsigned short tp_mac;
- unsigned short tp_net;
- unsigned int tp_sec;
- unsigned int tp_usec;
+ uint64_t tp_status;
+ unsigned int tp_len;
+ unsigned int tp_snaplen;
+ unsigned short tp_mac;
+ unsigned short tp_net;
+ unsigned int tp_sec;
+ unsigned int tp_usec;
};
+
+/*
+ * We use this internally as the tpacket version for TPACKET_V1 in
+ * 32-bit code on a 64-bit kernel.
+ */
#define TPACKET_V1_64 99
union thdr {
@@ -3582,6 +3591,36 @@ init_tpacket(pcap_t *handle, int version, const char *version_str)
#endif /* defined HAVE_TPACKET2 || defined HAVE_TPACKET3 */
/*
+ * If the instruction set for which we're compiling has both 32-bit
+ * and 64-bit versions, and Linux support for the 64-bit version
+ * predates TPACKET_V2, define ISA_64_BIT as the .machine value
+ * you get from uname() for the 64-bit version. Otherwise, leave
+ * it undefined. (This includes ARM, which has a 64-bit version,
+ * but Linux support for it appeared well after TPACKET_V2 support
+ * did, so there should never be a case where 32-bit ARM code is
+ * running o a 64-bit kernel that only supports TPACKET_V1.)
+ *
+ * If we've omitted your favorite such architecture, please contribute
+ * a patch. (No patch is needed for architectures that are 32-bit-only
+ * or for which Linux has no support for 32-bit userland - or for which,
+ * as noted, 64-bit support appeared in Linux after TPACKET_V2 support
+ * did.)
+ */
+#if defined(__i386__)
+#define ISA_64_BIT "x86_64"
+#elif defined(__ppc__)
+#define ISA_64_BIT "ppc64"
+#elif defined(__sparc__)
+#define ISA_64_BIT "sparc64"
+#elif defined(__s390__)
+#define ISA_64_BIT "s390x"
+#elif defined(__mips__)
+#define ISA_64_BIT "mips64"
+#elif defined(__hppa__)
+#define ISA_64_BIT "parisc64"
+#endif
+
+/*
* Attempt to set the socket to version 3 of the memory-mapped header and,
* if that fails because version 3 isn't supported, attempt to fall
* back to version 2. If version 2 isn't supported, just leave it at
@@ -3654,6 +3693,7 @@ prepare_tpacket_socket(pcap_t *handle)
handlep->tp_version = TPACKET_V1;
handlep->tp_hdrlen = sizeof(struct tpacket_hdr);
+#ifdef ISA_64_BIT
/*
* 32-bit userspace + 64-bit kernel + TPACKET_V1 are not compatible with
* each other due to platform-dependent data type size differences.
@@ -3663,13 +3703,33 @@ prepare_tpacket_socket(pcap_t *handle)
* version of the data structures.
*/
if (sizeof(long) == 4) {
- struct utsname utsname;
- uname(&utsname);
- if (!strcmp("x86_64", utsname.machine)) {
- handlep->tp_version = TPACKET_V1_64;
- handlep->tp_hdrlen = sizeof(struct tpacket_hdr_64);
- }
+ /*
+ * This is 32-bit code.
+ */
+ struct utsname utsname;
+
+ if (uname(&utsname) == -1) {
+ /*
+ * Failed.
+ */
+ snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+ "uname failed: %s", pcap_strerror(errno));
+ return -1;
+ }
+ if (strcmp(utsname.machine, ISA_64_BIT) == 0) {
+ /*
+ * uname() tells us the machine is 64-bit,
+ * so we presumably have a 64-bit kernel.
+ *
+ * XXX - this presumes that uname() won't lie
+ * in 32-bit code and claim that the machine
+ * has the 32-bit version of the ISA.
+ */
+ handlep->tp_version = TPACKET_V1_64;
+ handlep->tp_hdrlen = sizeof(struct tpacket_hdr_64);
+ }
}
+#endif
return 1;
}