diff --git a/CHANGES b/CHANGES index 89e739c46fa2..d174cd5e1a9c 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,432 @@ +Thursday, January 12, 2023 / The Tcpdump Group + Summary for 1.10.3 libpcap release + Source code: + Sort the PUBHDR variable in Makefile.in in "ls" order. + Fix typo in comment in pflog.h. + Remove two no-longer-present files from .gitignore. + Update code and comments for handling failure to set promiscuous + mode based on new information. + Building and testing: + install: Fixed not to install the non-public pcap-util.h header. + pcap-config: add a --version flag. + Makefile.in: Add some missing files in the distclean target. + +Saturday, December 31, 2022 / The Tcpdump Group + Summary for 1.10.2 libpcap release + Source code: + Use __builtin_unreachable() in PCAP_UNREACHABLE. + Use AS_HELP_STRING macro instead of AC_HELP_STRING in the + configure scripts, to avoid deprecation warnings. + Change availability tags in pcap.h to make it easier to + arrange for it to be used in Darwin releases. + Use AS_HELP_STRING for --enable-remote. + Fix some formatting string issues found by cppcheck. + Various small code and comment cleanups. + Use PCAP_ERROR (defined as -1) rather than explicit -1 for + functions the documentation says return PCAP_ERROR. + Remove unused code from the filter compiler. + Use _declspec(deprecated(msg)) rather than __pragma(deprecated) + for Windows deprecation warnings, so the message that was + specified shows up. + diag-control.h: define PCAP_DO_PRAGMA() iff we're going to use it. + Use "%d" to print some signed ints. + Use the Wayback Machine for a removed document in a comment. + Add some const qualifiers. + RDMA: Use PRIu64 to print a uint64_t. + "Dead" pcap_ts from pcap_open_dead() and ..._with_tstamp_precision(): + Don't crash if pcap_breakloop() is called. + Savefiles: + Fix pcap_dispatch() to return number of packets processed, rather + than 0, even at EOF. + If we get an error writing the packet header, don't write the + packet data. + Put PFLOG UID and PID values in the header into host byte order + when reading a LINKTYPE_PFLOG file. + Put CAN ID field in CAN pseudo-headers for LINUX_SLL2, as we do + for LINUX_SLL. + Fix inorrectly-computed "real" length for isochronous USB + transfers when reading savefiles. + Don't crash if pcap_can_set_rfmon() is called. + Fix pcap_offline_read() loop. + Capture: + Never process more than INT_MAX packets in a pcap_dispatch() call, + to avoid integer overflow (issue #1087). + Improve error messages for "no such device" and "permission + denied" errors. + SITA: Fix a typo in a variable name. + Packet filtering: + Get PFLOG header length from the length value in the header. + Support all the direction, reason, and action types supported by + all systems that support PFLOG. + Don't require PFLOG support on the target machine in order to + support PFLOG filtering (also fixes issue #1076). + Expand abbreviations into "proto X" properly. + gencode.c: Update a comment about the VLAN TPID test. + Add the minimum and maximum matching DLTs to an error message. + Linux: + Fix memory leak in capture device open (pull request #1038). + Fix detection of CAN/CAN FD packets in direction check (issue + #1051). + Fix double-free crashes on errors such as running on a kernel with + CONFIG_PACKET_MMAP not configured (issue #1054). + Use DLT_CAN_SOCKETCAN for CANbus interfaces (issue #1052; includes + changes from pull request #1035). + Make sure the CANFD_FDF can be relied on to indicate whether a + CANbus packet is a CAN frame or a CAN FD frame + Improve error message for "out of memory" errors for kernel + filters (see issue #1089). + Fix pcap_findalldevs() to find usbmon devices. + Fix handling of VLAN tagged packets if the link-layer type is + changed from DLT_LINUX_SLL to DLT_LINUX_SLL2 (see issue #1105). + Always turn on PACKET_AUXDATA (see issue #1105). + We require 2.6.27 or later, so PACKET_RESERVE is available. + Make sure there's reserved space for a DLT_LINUX_SLL2 header + when capturing. + Correctly compute the "real" length for isochronous USB transfers. + Don't have an eventfd descriptor open in non-blocking mode, so as + not to waste descriptors. + netfilter: Squelch a narrowing warning (To be look at before 2038). + BPF capture (*BSD, macOS, AIX, Solaris 11): + Fix case where a device open might fail, rather than falling back + to a smaller buffer size, when the initial buffer size is too + big. + Use an unsigned device number to iterate over BPF devices, to + squelch a compiler warning. + NetBSD: + Fix handling of LINKTYPE_HDLC/DLT_HDLC. + rpcap: + Fix unaligned accesses in rpcapd (pull request #1037). + Fix code to process port number. + Clean up findalldevs code in rpcapd. + Clean up bufferizing code. + Fix a file descriptor/handle leak in pcap_findalldevs_ex() + (Coverity CID 1507240). + Improve error messages for host and port resolution errors. + Fix connect code not to fail if both IPv4 and IPv6 addresses are + tried. + Improve connect failure error message. + Provide an error message for a bad authentication reply size. + For link-layer types with host-endian fields in the header, fix + those fields if capturing from a server with a different byte + order. + Suppress temporarily the warnings with "enable remote packet capture". + Windows: + Add support for NdisMediumIP (pull request #1027). + Don't require applications using pcap to be built with VS 2015 or + later. + Use the correct string for the DLL VersionInfo. + Remove unnecessary DllMain() function. + Correctly handle ERROR_INVALID_FUNCTION from + PacketGetTimestampModes() (indicate that WinPcap or an older + version of Npcap is probably installed). + Fix use-after-free in some cases when a pcap_t is closed. + Make sure an error is returned by pcap_create_interface() if + PacketOpenAdapter() fails. + Return an error if the driver reports 0 timestamp modes supported. + Close the ADAPTER handle for some errors in + pcap_create_interface(). + Get rid of old umaintained VS project files. + Fix deprecation warning for pcap_handle(). + Npcap is now at npcap.com, not npcap.org. + Make sure "no such device" and "no permission to open device" + errors show up in pcap_activate(), not pcap_create() (fixes, + among other things, tcpdump -i ). + npcap: squelch deprecation warnings for kernel dump mode. + Haiku: + Implement pcap_lib_version(), as now required. + Handle negative or too-large snaplen values. + Fix various build issues and warnings. + Building and testing: + Update configure-time universal build checks for macOS. + Update config.guess and config.sub. + If we look for an SSL library with pkg-config in configure script, + try pkg-config first. + If we have pkg-config and Homebrew, try to set pkg-config up to + find Homebrew packages. + Handle some Autoconf/make errors better. + Use "git archive" for the "make releasetar" process. + Remove the release candidate rcX targets. + Fix compiling on Solaris 9/SPARC and 11/AMD64. + Address assorted compiler warnings. + Fix cross-building on Linux for Windows with mingw32 for Win64 + (pull request #1031). + Properly set installation directory on Windows when not compiling + with MSVC. + Fix configure script checks for compiler flags. + Give more details if check for usable (F)Lex fails. + Fix compiling with GCC 4.6.4. + Don't use add_compile_options() with CMake, as we currently don't + require 2.8.12, where it first appeared. + Don't provide -L/usr/lib for pkg-config --libs in pkg-config. + Fix error message for inadequate Bison/Berkeley YACC. + configure: correctly do some DPDK checks. + Only use pkg-config when checking for DPDK. + Allow the path in which DPDK is installed to be specified. + Use pkg-config first when checking for libibverbs. + CMake: fix check for libibverbs with Sun's C compiler. + Have CMake warn if no capture mechanism can be found. + Don't do stuff requiring 3.19 or later on earlier CMakes. + Squelch some CMake warnings. + Fix diag-control.h to handle compiling with clang-cl (issues + #1101 and #1115). + Cleanup various leftover cruft in the configure script. + Fix building without protochain support. (GH #852) + Check for a usable YACC (or Bison) and {F}lex in CMake, as we do + in autotools. + Only check for a C++ compiler on Haiku, as that's the only + platform with C++ code, and make sure they generate code for + the same instruction set bit-width (both 32-bit or both 64-bit) + (issue #1112). + On Solaris, check the target bit-width and set PKG_CONFIG_PATH + appropriately, to handle the mess that is the D-Bus library + package (issue #1112). + Fix generation of pcap-config and libpcap.pc files (issue #1062). + pcap-config: don't assume the system library directory is /usr/lib. + pcap-config: add a --static-pcap-only flag. + Cirrus CI: Use the same configuration as for the main branch. + Add four libpcap test files. + Update Npcap SDK to 1.13. + Makefile.in: Use TEST_DIST, like for tcpdump. + Remove awk code from mkdep. + Cirrus CI: Add the libssl-dev package in the Linux task. + Cirrus CI: Add the openssl@3 brew package in the macOS task. + Get "make shellcheck" to pass again. + CMake: Build valgrindtest only if Autoconf would. + CMake: use ${CMAKE_INSTALL_SBINDIR} rather than just sbin. + CMake: use NUL: as the null device on Windows. + autoconf: fix typo in test of macOS version. + Makefile.in: Add two missing files in EXTRA_DIST. + autotools, cmake: provide an rpath option if necessary. + configure: get rid of the attempt to auto-run PKG_PROG_PKG_CONFIG. + configure: use PKG_CHECK_MODULES to run pkg-config. + Documentation: + Add README.solaris.md. + Add SCTP to pcap-filter(7). + Note that = and == are the same operator in filters (issue #1044). + Update INSTALL.md, README.md, and README.solaris.md. + Update and clean up CONTRIBUTING.md. + Trim documentation of support for now-dead UN*Xe and older + versions of other UN*Xes. + Move the "how to allocate a LINKTYPE_/DLT_ value" documentation to + the web site. + Clean up man pages. + Move README.capture-module to the web site. + Improve some protocol details in pcap-filter(7). + Refine "relop" notes in pcap-filter(7). + In pcap-filter(7) "domain" is an id. + Discuss backward compatibility in pcap-filter(7). + Other improvements to pcap-filter(7). + Document pcap_breakloop(3PCAP) interaction with threads better. + Document PCAP_ERROR_NOT_ACTIVATED for more routines. + +Wednesday, June 9, 2021: + Summary for 1.10.1 libpcap release: + Packet filtering: + Fix "type XXX subtype YYY" giving a parse error + Source code: + Add PCAP_AVAILABLE_1_11. + Building and testing: + Rename struct bpf_aux_data to avoid NetBSD compile errors + Squelch some compiler warnings + Squelch some Bison warnings + Fix cross-builds with older kernels lacking BPF_MOD and BPF_XOR + Fix Bison detection for minor version 0. + Fix parallel build with FreeBSD make. + Get DLT_MATCHING_MAX right in gencode.c on NetBSD. + Define timeradd() and timersub() if necessary. + Fix Cygwin/MSYS target directories. + Fix symlinking with DESTDIR. + Fix generation of libpcap.pc with CMake when not building a shared + library. + Check for Arm64 as well as x86-64 when looking for packet.lib on + Windows. + Documentation: + Refine Markdown in README.md. + Improve the description of portrange in filters. + README.linux.md isn't Markdown, rename it just README.linux. + pcapng: + Support reading version 1.2, which some writers produce, and which + is the same as 1.0 (some new block types were added, but + that's not sufficient reason to bump the minor version number, + as code that understands those new block types can handle them + in a 1.0 file) + Linux: + Drop support for text-mode USB captures, as we require a 2.6.27 + or later kernel (credit to Chaoyuan Peng for noting the + sscanf vulnerabilities in the text-mode code that got me to + realize that we didn't need this code any more) + Bluetooth: fix non-blocking mode. + Don't assume that all compilers used to build for Linux support + the __atomic builtins + Windows: + Add more information in "interface disappeared" error messages, in + the hopes of trying to figure out the cause. + Treat ERROR_DEVICE_REMOVED as "device was removed". + Indicate in the error message which "device was removed" error + occurred. + Report the Windows error status if PacketSendPacket() fails. + Use %lu for ULONGs in error message formats. + Don't treat the inability to find airpcap.dll as an error. + Ignore spurious error reports by Microsoft Surface mobile + telephony modem driver + rpcap: + Clean up error checking and error messages for server address + lookup. + +Tuesday, December 29, 2020 + Summary for 1.10.0 libpcap release + Add support for capturing on DPDK devices + Label most APIs by the first release in which they're available + Fix some memory leaks, including in pcap_compile() + Add pcap_datalink_val_to_description_or_dlt() + Handle the pcap private data in a fashion that makes fewer + assumptions about memory layouts (might fix GitHub issue #940 + on ARM) + Fix some thread safety issues + pcap_findalldevs(): don't sort interfaces by unit number + Always return a list of supported time-stamp types, even if only + host time stamps are supported + Increase the maximum snaplen for LINKTYPE_USBPCAP/DLT_USBPCAP + Report the DLT description in error messages + Add pcap_init() for first-time initialization and global option + setting; it's not required, but may be used + Remove (unused) SITA support + Capture file reading: + Correctly handle pcapng captures with more than one IDB with a + snspshot length greater than the supported maximum + Capture file writing: + Create the file in pcap_dump_open_append() if it doesn't exist + Packet filtering: + Fix "unknown ether proto 'aarp'" + Add a new filter "ifindex" for DLT_LINUX_SLL2 files on all + platforms and live Linux captures + Add a hack to the optimizer to try to catch certain optimizer + loops (should prevent GitHub issue #112) + Show special Linux BPF offsets symbolically in bpf_image() and + bpf_dump() + Added support for ICMPv6 types 1-4 as tokens with names + Remove undocumented and rather old "ether proto" protocols + Catch invalid IPv4 addresses in filters + Don't assume ARM supports unaligned accesses + Security and other issues found by analysis: + Fix various security issues reported by Charles Smith at Tangible + Security + Fix various security issues reported by Include Security + Fix some issues found by cppcheck. + Add some overflow checks in the optimizer + rpcap: + Support rpcap-over-TLS + Redo protocol version negotiation to avoid problems with old + servers (it still works with servers using the old negotiation, + as well as servers not supporting negotiation) + Error handling cleanups + Add some new authentication libpcap error codes for specific + errors + Fix some inetd issues in rpcapd + Fix rpcapd core dumps with invalid configuration file + On UN*X, don't have rpcapd tell the client why authentication + failed, so a brute-force attacker can't distinguish between + "unknown user name" and "known user name, wrong password" + Allow rpcapd to rebind more rapidly (GitHub issue #765) + Documentation: + Improve man pages, including adding backward compatibility notes + Building and testing: + Require, and assume, some level of C99 support in the C compiler + Require Visual Studio 2015 or later if using Visual Studio + Fix configure script issues, including with libnl on Linux + Fix CMake issues + Squelch complaints from Bison about "%define api.pure" being + deprecated + Fix compilation of pcap-tc.c + Linux: + Require PF_PACKET support, and kernel 2.6.27 or later + Handle systems without AF_INET or AF_UNIX socket support + Get rid of Wireless Extensions for turning monitor mode on + Proper memory sync for PACKET_MMAP (may prevent GitHub issue + #898) + Drop support for libnl 1 and 2. + Return error on interface going away, but not if it just went + down but is still present + Set socket protocol only after packet ring configured, + reducing bogus packet drop reports + Get ifdrop stats from sysfs. + When adjusting BPF programs, do not subtract the + SLL[2]_HDR_LEN if the location is negative (special metadata + offset), to preserve references to metadata; see + https://github.com/the-tcpdump-group/tcpdump/issues/480#issuecomment-486827278 + Report a warning for unknown ARPHRD types + Have pcap_breakloop() forcibly break out of a sleeping + capture loop + Add support for DSA data link types + For raw USB bus capture, use the snapshot length to set the + buffer size, and set the len field to reflect the length + in the URB (GitHub issue #808) + With a timeout of zero, wait indefinitely + Clean up support for some non-GNU libc C libraries + Add DLT_LINUX_SLL2 for cooked-mode captures + Probe CONFIGURATION descriptor of connected USB devices + Treat EPERM on ethtool ioctls as meaning "not supported", as + permissions checks are done before checking whether the + ioctl is supported at all + macOS: + Cope with getting EPWROFF from SIOCGIFMEDIA + Treat EPERM on SIOCGIFMEDIA as meaning "not supported", as + permissions checks are done before checking whether the + ioctl is supported at all + Treat ENXIO when reading packets as meaning "the interface + was removed" + Report "the interface disappeared", not "the interface went + down", if the interface was removed during a capture + FreeBSD: + Treat ENXIO as meaning "the interface was removed" + Report "the interface disappeared", not "the interface went + down", if the interface was removed during a capture + NetBSD: + Treat ENXIO as meaning "the interface was removed" + Report "the interface disappeared", not "the interface went + down", if the interface was removed during a capture + OpenBSD: + Treat EIO as meaning "the interface was removed" + Report "the interface disappeared", not "the interface went + down", if the interface was removed during a capture + DragonFly BSD: + Treat ENXIO as meaning "the interface was removed" + Report "the interface disappeared", not "the interface went + down", if the interface was removed during a capture + Solaris: + Treat ENXIO as meaning "the interface was removed" + Report "the interface disappeared", not "the interface went + down", if the interface was removed during a capture + AIX: + Fix loading of BPF kernel extension + Treat ENXIO as meaning "the interface was removed" + Report "the interface disappeared", not "the interface went + down", if the interface was removed during a capture + Windows: + Make the snapshot length work even if pcap_setfilter() + isn't called + Fix compilation on Cygwin/MSYS + Add pcap_handle(), and deprecate pcap_fileno() + Report PCAP_ERROR_NO_SUCH_DEVICE for a non-existent device + Return an appropriate error message for device removed or + device unusable due to a suspend/resume + Report a warning for unknown NdisMedium types + Have pcap_breakloop() forcibly break out of a sleeping + capture loop + Clean up building DLL + Handle CRT mismatch for pcap_dump_fopen() + Map NdisMediumWirelessWan to DLT_RAW + Add AirPcap support in a module, rather than using + WinPcap/Npcap's support for it + Report the system error for PacketSetHwFilter() failures + Add support for getting and setting packet time stamp types + with Npcap + Have pcap_init() allow selecting whether the API should use + local code page strings or UTF-8 strings (including error + messages) + Haiku: + Add capture support + Sunday, July 22, 2018 Summary for 1.9.1 libpcap release Mention pcap_get_required_select_timeout() in the main pcap man page @@ -26,7 +455,7 @@ Sunday, July 22, 2018 need to be Fix reading of capture statistics for Linux USB Fix packet size values for Linux USB packets (GitHub issue #808) - Check only VID in VLAN test in filterss (GitHub issue #461) + Check only VID in VLAN test in filters (GitHub issue #461) Fix pcap_list_datalinks on 802.11 devices on macOS Fix overflows with very large snapshot length in pcap file Improve parsing of rpcapd configuration file (GitHub issue #767) @@ -67,7 +496,6 @@ Sunday, July 22, 2018 Boost the TPACKET_V3 timeout to the maximum if a timeout of 0 was specified Five CVE-2019-15161, CVE-2019-15162, CVE-2019-15163, CVE-2019-15164, CVE-2019-15165 - Fixes for CVE-2018-16301, errors in pcapng reading. PCAPNG reader applies some sanity checks before doing malloc(). Sunday, June 24, 2018, by mcr@sandelman.ca @@ -75,7 +503,7 @@ Sunday, June 24, 2018, by mcr@sandelman.ca Added testing system to libpcap, independent of tcpdump Changes to how pcap_t is activated Adding support for Large stream buffers on Endace DAG cards - Changes to BSD 3-clause license to 2-clause licence + Changes to BSD 3-clause license to 2-clause license Additions to TCP header parsing, per RFC3168 Add CMake build process (extensive number of changes) Assign a value for OpenBSD DLT_OPENFLOW. @@ -93,7 +521,7 @@ Sunday, June 24, 2018, by mcr@sandelman.ca Make VLAN filter handle both metadata and inline tags D-Bus captures can now be up to 128MB in size Added LORATAP DLT value - Added DLT_VSOCK for http://qemu-project.org/Features/VirtioVsock + Added DLT_VSOCK for https://qemu-project.org/Features/VirtioVsock probe_devices() fixes not to overrun buffer for name of device Add linux-specific pcap_set_protocol_linux() to allow specifying a specific capture protocol. RDMA sniffing support for pcap @@ -275,7 +703,7 @@ Summary for 1.5.0 libpcap release than the mcr repository Checks added for malloc()/realloc()/etc. failures Fixed build on Solaris 11 - Support filtering filtering E1 SS7 traffic on MTP2 layer Annex A + Support filtering E1 SS7 traffic on MTP2 layer Annex A Use "ln -s" to link man pages by default Add support for getting nanosecond-resolution time stamps when capturing and reading capture files @@ -336,7 +764,7 @@ Summary for 1.3.0 libpcap release Friday December 9, 2011. guy@alum.mit.edu. Summary for 1.2.1 libpcap release Update README file. - Fix typoes in README.linux file. + Fix typos in README.linux file. Clean up some compiler warnings. Fix Linux compile problems and tests for ethtool.h. Treat Debian/kFreeBSD and GNU/Hurd as systems with GNU @@ -369,7 +797,7 @@ Summary for 1.2 libpcap release Noted real nature of LINKTYPE_ARCNET. Add a link-layer type for DVB-CI. Fix configure-script discovery of VLAN acceleration support. - see http://netoptimizer.blogspot.com/2010/09/tcpdump-vs-vlan-tags.html + see https://netoptimizer.blogspot.com/2010/09/tcpdump-vs-vlan-tags.html Linux, HP-UX, AIX, NetBSD and OpenBSD compilation/conflict fixes. Protect against including AIX 5.x's having been included. Add DLT_DBUS, for raw D-Bus messages. @@ -568,7 +996,7 @@ Tue. September 19, 2006. ken@xelerance.com. Summary for 0.9.5 libpcap release beginning+link-layer Add DLT/LINKTYPE for carrying FRF.16 Multi-link Frame Relay Fix allocation of buffer for list of link-layer types - Added a new DLT and LINKTYPE value for ARINC 653 Interpartition Communcation Messages + Added a new DLT and LINKTYPE value for ARINC 653 Interpartition Communication Messages Fixed a typo in a DLT value: it should start with DLT_ and not LINKTYPE_ Redefined DLT_CAN20B and LINKTYPE_CAN20B as #190 (as this is the right value for CAN). Added definition for DLT_A429 and LINKTYPE_A429 as #184. @@ -682,7 +1110,7 @@ Tuesday January 9, 2001. guy@alum.mit.edu. Summary for 0.6 release Header files fixed to allow use in C++ programs. - Removed dependancy on native headers for packet layout. + Removed dependency on native headers for packet layout. Removed Linux specific headers that were shipped. Security fixes: Strcpy replaced with strlcpy, sprintf replaced @@ -820,7 +1248,7 @@ v0.3 Sat Nov 30 20:56:27 PST 1996 v0.2.1 Sun Jul 14 03:02:26 PDT 1996 -- Fixes for HP-UX 10. Thanks in part to to Thomas Wolfram +- Fixes for HP-UX 10. Thanks in part to Thomas Wolfram (wolf@prz.tu-berlin.de) and Rick Jones (raj@hpisrdq.cup.hp.com) - Added support for SINIX. Thanks to Andrej Borsenkow diff --git a/CMakeLists.txt b/CMakeLists.txt index 55b93f14d74c..58c5159905d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,11 @@ -cmake_minimum_required(VERSION 2.8.6) +if(WIN32) + # + # We need 3.12 or later, so that we can set policy CMP0074; see + # below. + cmake_minimum_required(VERSION 3.12) +else(WIN32) + cmake_minimum_required(VERSION 2.8.6) +endif(WIN32) # # Apple doesn't build with an install_name starting with @rpath, and @@ -9,44 +16,308 @@ if(POLICY CMP0042) cmake_policy(SET CMP0042 OLD) endif() +# +# Squelch noise about quoted strings in if() statements. +# WE KNOW WHAT WE'RE DOING, WE'RE DOING EVERYTHING THE WAY THAT NEWER +# VERSIONS OF CMAKE EXPECT BY DEFAULT, DON'T WASTE OUR TIME WITH NOISE. +# +if(POLICY CMP0054) + cmake_policy(SET CMP0054 NEW) +endif() + +# +# We want find_file() and find_library() to honor {packagename}_ROOT, +# as that appears to be the only way, with the Visual Studio 2019 IDE +# and its CMake support, to tell CMake where to look for the Npcap +# or WinPcap SDK. +# +if(POLICY CMP0074) + cmake_policy(SET CMP0074 NEW) +endif() + +# +# We want check_include_file() to honor CMAKE_REQUIRED_LIBRARIES; see +# the big comment before the check_include_file() test for +# infiniband/verbs.h for the reason. +# +if(POLICY CMP0075) + cmake_policy(SET CMP0075 NEW) +endif() + set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules) -project(pcap) +# +# We only need a C++ compiler for Haiku; all code except for its +# pcap module is in C. +# +# We do that by specifying just C in the project() call and, after +# that finishes, checking for Haiku and, if we're building for +# Haiku, use enable_language() to check for C++. This means that +# we don't require a C++ compiler on platforms other than Haiku. +# +# CMAKE_SYSTEM_NAME is set by project(), so we can't do this by +# testing CMAKE_SYSTEM_NAME and then passing different language +# lists to project() based on the system. +# +project(pcap C) # -# Try to enable as many C99 features as we can. -# At minimum, we want C++/C99-style // comments. +# For getting raw lists of --libs and --libs --static information from a +# pkg-config module. # -# Newer versions of compilers might default to supporting C99, but older -# versions may require a special flag. +# In CMake up to 2.8.12, pkg_check_modules() sets: # -# Prior to CMake 3.1, setting CMAKE_C_STANDARD will not have any effect, -# so, unless and until we require CMake 3.1 or later, we have to do it -# ourselves on pre-3.1 CMake, so we just do it ourselves on all versions -# of CMake. +# _LIBRARIES, which is a list of library names to which, on +# a UN*X, -l can be prefixed - i.e., names, without extensions, +# rather than full paths to the file. +# _LIBRARY_DIRS, which is a list of paths to directories +# containing the libraries, to which, on a UN*X, -L can be +# prefixed. +# _LDFLAGS, which is a list of *all* required linker flags +# _LDFLAGS_OTHER, which is a list of all linker flags other +# than -l and -L flags # -# Note: with CMake 3.1 through 3.5, the only compilers for which CMake -# handles CMAKE_C_STANDARD are GCC and Clang. 3.6 adds support only -# for Intel C; 3.9 adds support for PGI C, Sun C, and IBM XL C, and -# 3.10 adds support for Cray C and IAR C, but no version of CMake has -# support for HP C. Therefore, even if we use CMAKE_C_STANDARD with -# compilers for which CMake supports it, we may still have to do it -# ourselves on other compilers. +# In 3.0 (at least as of 3.0.2), it also sets: # -# See the CMake documentation for the CMAKE__COMPILER_ID variables -# for a list of compiler IDs. +# _LINK_LIBRARIES, which is a list of full paths to the +# library files. # -# We don't worry about MSVC; it doesn't have such a flag - either it -# doesn't support the C99 features we need at all, or it supports them -# regardless of the compiler flag. +# but if is _STATIC, _LINK_LIBRARIES is +# currently not set by CMake. # -# XXX - this just tests whether the option works and adds it if it does. -# We don't test whether it's necessary in order to get the C99 features -# that we use; if we ever have a user who tries to compile with a compiler -# that can't be made to support those features, we can add a test to make -# sure we actually *have* C99 support. +# Unfortunately, pkg_check_modules() sets the +# PKG_CONFIG_ALLOW_SYSTEM_LIBS environment variable when running +# pkg-config, so the output of --libs, etc. may include a -L for the +# system library, which we do *NOT* want to put in our libpcap.pc and +# pcap-config files. # +# So we just run pkg-config ourselves, so that we get its output +# directly without any processing by CMake. +# +macro(pkg_get_link_info _prefix _package) + if (PKG_CONFIG_EXECUTABLE) + # + # Get the --libs information. + # + # We force PKG_CONFIG_ALLOW_SYSTEM_LIBS to be undefined, as + # at least some versions of CMake appear to define it in + # pkg_check_modules() before running pkg-config and *not* undefine + # it after running it. + # + unset(ENV{PKG_CONFIG_ALLOW_SYSTEM_LIBS}) + set(_pkg_config_result "") + execute_process( + COMMAND ${PKG_CONFIG_EXECUTABLE} "--libs" ${_package} + OUTPUT_VARIABLE _pkg_config_result + RESULT_VARIABLE _pkg_config_failed + OUTPUT_STRIP_TRAILING_WHITESPACE) + + if (_pkg_config_failed) + # + # pkg-config failed; assume that means that there is no such + # package for it to find. XXX - what do we do here? + # + set(${_prefix}_FOUND_WITH_PKG_CONFIG FALSE) + else() + # + # pkg-config succeeded; replace CR and LF with spaces. + # + string(REGEX REPLACE "[\r\n]" " " ${_prefix}_LIBS "${_pkg_config_result}") + + # + # Now get the --libs --static information. + # + set(_pkg_config_result "") + execute_process( + COMMAND ${PKG_CONFIG_EXECUTABLE} "--libs" "--static" ${_package} + OUTPUT_VARIABLE _pkg_config_result + RESULT_VARIABLE _pkg_config_failed + OUTPUT_STRIP_TRAILING_WHITESPACE) + + if (_pkg_config_failed) + # + # pkg-config failed; assume that means that there is no such + # package for it to find. XXX - what do we do here? + # + set(${_prefix}_FOUND_WITH_PKG_CONFIG FALSE) + else() + # + # pkg-config succeeded; replace CR and LF with spaces. + # + string(REGEX REPLACE "[\r\n]" " " ${_prefix}_LIBS_STATIC "${_pkg_config_result}") + + # + # List this package in its PACKAGE_NAME variable. + # + set(${_prefix}_PACKAGE_NAME "${_package}") + + # + # It worked. + # + set(${_prefix}_FOUND_WITH_PKG_CONFIG TRUE) + endif() + endif() + endif() +endmacro() + +macro(get_link_info_from_library_path _library_prefix _library_name) + if(NOT ${_library_prefix}_LIBRARY STREQUAL "${_library_prefix}_LIBRARY-NOTFOUND") + get_filename_component(_lib_directory "${${_library_prefix}_LIBRARY}}" DIRECTORY) + + # + # The closest thing to a list of "system library directories" in + # which the linker will, by default, search for libraries appears to + # be CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES, so that's what we use + # when we're trying to construct a -L argument, for insertion into + # pcap-config and libpcap.pc, for a library upon which we depend. + # + # In some versions of CMake it appears to have duplicate entries, + # but that shouldn't affect a search for a directory in that list. + # + list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${_lib_directory}" _lib_index) + if(_lib_index EQUAL -1) + # + # No, so add a -L flag to get the linker to search in that + # directory. + # + set(${_library_prefix}_LIBS "-L${_lib_directory}") + set(${_library_prefix}_LIBS_STATIC "-L${_lib_directory}") + set(${_libraryprefix}_LIBS_PRIVATE "-L${_lib_directory}") + endif() + set(${_library_prefix}_LIBS "${${_library_prefix}_LIBS} -l${_library_name}") + set(${_library_prefix}_LIBS_STATIC "${${_library_prefix}_LIBS} -l${_library_name}") + set(${_library_prefix}_LIBS_PRIVATE "${${_library_prefix}_LIBS} -l${_library_name}") + endif() +endmacro() + +if(CMAKE_SYSTEM_NAME STREQUAL "Haiku") + enable_language(CXX) + + # + # OK, this is a royal pain. + # + # CMake will try to determine the sizes of some data types, including + # void *, early in the process of configuration; apparently, it's done + # as part of processing the project() command. + # + # At least as of CMake 2.8.6, it does so by checking the size of + # "void *" in C, setting CMAKE_C_SIZEOF_DATA_PTR based on that, + # setting CMAKE_SIZEOF_VOID_P to that, and then checking the size + # of "void *" in C++, setting CMAKE_CXX_SIZEOF_DATA_PTR based on + # that, and then setting CMAKE_SIZEOF_VOID_P to *that*. + # + # The compile tests include whatever C flags may have been provided + # to CMake in the CFLAGS and CXXFLAGS environment variables. + # + # If you set an architecture flag such as -m32 or -m64 in CFLAGS + # but *not* in CXXFLAGS, the size for C++ will win, and hilarity + # will ensue. + # + # Or if, at least on Solaris, you have a newer version of GCC + # installed, but *not* a newer version of G++, and you have Oracle + # Studio installed, it will find GCC, which will default to building + # 64-bit, and Oracle Studio's C++ compiler, which will default to + # building 32-bit, the size for C++ will win, and, again, hilarity + # will ensue. + # + # So we make sure both languages have the same pointer sizes with + # the flags they're given; if they don't, it means that the + # compilers for the languages will, with those flags, not produce + # code that can be linked together. + # + # This is unlikely to happen on Haiku, but it *has* happened on + # Solaris; we do this for future-proofing, in case we ever need + # C++ on a platform where that can happen. + # + if(NOT ${CMAKE_C_SIZEOF_DATA_PTR} EQUAL ${CMAKE_CXX_SIZEOF_DATA_PTR}) + message(FATAL_ERROR +"C compiler ${CMAKE_C_COMPILER} produces code with \ +${CMAKE_C_SIZEOF_DATA_PTR}-byte pointers while C++ compiler \ +${CMAKE_CXX_COMPILER} produces code with \ +${CMAKE_CXX_SIZEOF_DATA_PTR}-byte pointers. \ +This prevents code in these languages from being combined.") + endif() +endif() + +# +# Show the bit width for which we're compiling. +# This can help debug problems if you're dealing with a compiler that +# defaults to generating 32-bit code even when running on a 64-bit +# platform, and where that platform may provide only 64-bit versions of +# libraries that we might use (looking at *you*, Oracle Studio!). +# +if(CMAKE_SIZEOF_VOID_P EQUAL 4) + message(STATUS "Building 32-bit") +elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) + message(STATUS "Building 64-bit") +endif() + +# +# Solaris pkg-config is annoying. For at least one package (D-Bus, I'm +# looking at *you*!), there are separate include files for 32-bit and +# 64-bit builds (I guess using "unsigned long long" as a 64-bit integer +# type on a 64-bit build is like crossing the beams or soething), and +# there are two separate .pc files, so if we're doing a 32-bit build we +# should make sure we look in /usr/lib/pkgconfig for .pc files and if +# we're doing a 64-bit build we should make sure we look in +# /usr/lib/amd64/pkgconfig for .pc files. +# +if(CMAKE_SYSTEM_NAME STREQUAL "SunOS" AND CMAKE_SYSTEM_VERSION MATCHES "5[.][0-9.]*") + # + # Note: string(REPLACE) does not appear to support using ENV{...} + # as an argument, so we set a variable and then use set() to set + # the environment variable. + # + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + # + # 64-bit build. If /usr/lib/pkgconfig appears in the path, + # prepend /usr/lib/amd64/pkgconfig to it; otherwise, + # put /usr/lib/amd64 at the end. + # + if((NOT DEFINED ENV{PKG_CONFIG_PATH}) OR "$ENV{PKG_CONFIG_PATH}" EQUAL "") + # + # Not set, or empty. Set it to /usr/lib/amd64/pkgconfig. + # + set(fixed_path "/usr/lib/amd64/pkgconfig") + elseif("$ENV{PKG_CONFIG_PATH}" MATCHES "/usr/lib/pkgconfig") + # + # It contains /usr/lib/pkgconfig. Prepend + # /usr/lib/amd64/pkgconfig to /usr/lib/pkgconfig. + # + string(REPLACE "/usr/lib/pkgconfig" + "/usr/lib/amd64/pkgconfig:/usr/lib/pkgconfig" + fixed_path "$ENV{PKG_CONFIG_PATH}") + else() + # + # Not empty, but doesn't contain /usr/lib/pkgconfig. + # Append /usr/lib/amd64/pkgconfig to it. + # + set(fixed_path "$ENV{PKG_CONFIG_PATH}:/usr/lib/amd64/pkgconfig") + endif() + set(ENV{PKG_CONFIG_PATH} "${fixed_path}") + elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) + # + # 32-bit build. If /usr/amd64/lib/pkgconfig appears in the path, + # prepend /usr/lib/pkgconfig to it. + # + if("$ENV{PKG_CONFIG_PATH}" MATCHES "/usr/lib/amd64/pkgconfig") + # + # It contains /usr/lib/amd64/pkgconfig. Prepend + # /usr/lib/pkgconfig to /usr/lib/amd64/pkgconfig. + # + string(REPLACE "/usr/lib/amd64/pkgconfig" + "/usr/lib/pkgconfig:/usr/lib/amd64/pkgconfig" + fixed_path "$ENV{PKG_CONFIG_PATH}") + set(ENV{PKG_CONFIG_PATH} "${fixed_path}") + endif() + endif() +endif() + include(CheckCCompilerFlag) + +# +# For checking if a compiler flag works and adding it if it does. +# macro(check_and_add_compiler_option _option) message(STATUS "Checking C compiler flag ${_option}") string(REPLACE "=" "-" _temp_option_variable ${_option}) @@ -57,23 +328,105 @@ macro(check_and_add_compiler_option _option) endif() endmacro() +# +# If we're building with Visual Studio, we require Visual Studio 2015, +# in order to get sufficient C99 compatibility. Check for that. +# +# If not, try the appropriate flag for the compiler to enable C99 +# features. +# set(C_ADDITIONAL_FLAGS "") -if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR - CMAKE_C_COMPILER_ID MATCHES "Clang") - check_and_add_compiler_option("-std=gnu99") -elseif(CMAKE_C_COMPILER_ID MATCHES "XL") +if(MSVC) + if(MSVC_VERSION LESS 1900) + message(FATAL_ERROR "Visual Studio 2015 or later is required") + endif() + # - # We want support for extensions picked up for GNU C compatibility, - # so we use -qlanglvl=extc99. + # Treat source files as being in UTF-8 with MSVC if it's not using + # the Clang front end. + # We assume that UTF-8 source is OK with other compilers and with + # MSVC if it's using the Clang front end. # - check_and_add_compiler_option("-qlanglvl=extc99") -elseif(CMAKE_C_COMPILER_ID MATCHES "HP") - check_and_add_compiler_option("-AC99") -elseif(CMAKE_C_COMPILER_ID MATCHES "Sun") - check_and_add_compiler_option("-xc99") -elseif(CMAKE_C_COMPILER_ID MATCHES "Intel") - check_and_add_compiler_option("-c99") -endif() + if(NOT ${CMAKE_C_COMPILER} MATCHES "clang*") + set(C_ADDITIONAL_FLAGS "${C_ADDITIONAL_FLAGS} /utf-8") + endif(NOT ${CMAKE_C_COMPILER} MATCHES "clang*") +else(MSVC) + # + # For checking if a compiler flag works, failing if it doesn't, + # and adding it otherwise. + # + macro(require_and_add_compiler_option _option) + message(STATUS "Checking C compiler flag ${_option}") + string(REPLACE "=" "-" _temp_option_variable ${_option}) + string(REGEX REPLACE "^-" "" _option_variable ${_temp_option_variable}) + check_c_compiler_flag("${_option}" ${_option_variable}) + if(${${_option_variable}}) + set(C_ADDITIONAL_FLAGS "${C_ADDITIONAL_FLAGS} ${_option}") + else() + message(FATAL_ERROR "C99 support is required, but the compiler doesn't support a compiler flag to enable it") + endif() + endmacro() + + # + # Try to enable as many C99 features as we can. + # At minimum, we want C++/C99-style // comments. + # + # Newer versions of compilers might default to supporting C99, but + # older versions may require a special flag. + # + # Prior to CMake 3.1, setting CMAKE_C_STANDARD will not have any effect, + # so, unless and until we require CMake 3.1 or later, we have to do it + # ourselves on pre-3.1 CMake, so we just do it ourselves on all versions + # of CMake. + # + # Note: with CMake 3.1 through 3.5, the only compilers for which CMake + # handles CMAKE_C_STANDARD are GCC and Clang. 3.6 adds support only + # for Intel C; 3.9 adds support for PGI C, Sun C, and IBM XL C, and + # 3.10 adds support for Cray C and IAR C, but no version of CMake has + # support for HP C. Therefore, even if we use CMAKE_C_STANDARD with + # compilers for which CMake supports it, we may still have to do it + # ourselves on other compilers. + # + # See the CMake documentation for the CMAKE__COMPILER_ID variables + # for a list of compiler IDs. + # + # XXX - this just tests whether the option works, fails if it doesn't, + # and adds it if it does. We don't test whether it's necessary in order + # to get the C99 features that we use, or whether, if it's used, it + # enables all the features that we require. + # + if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR + CMAKE_C_COMPILER_ID MATCHES "Clang") + require_and_add_compiler_option("-std=gnu99") + elseif(CMAKE_C_COMPILER_ID MATCHES "XL") + # + # We want support for extensions picked up for GNU C compatibility, + # so we use -qlanglvl=extc99. + # + require_and_add_compiler_option("-qlanglvl=extc99") + elseif(CMAKE_C_COMPILER_ID MATCHES "HP") + require_and_add_compiler_option("-AC99") + elseif(CMAKE_C_COMPILER_ID MATCHES "Sun") + require_and_add_compiler_option("-xc99") + elseif(CMAKE_C_COMPILER_ID MATCHES "Intel") + require_and_add_compiler_option("-c99") + endif() +endif(MSVC) + +# +# If we're building with MinGW, we need to specify _WIN32_WINNT as +# 0x0600 ("NT 6.0", a/k/a Vista/Windows Server 2008) or higher +# in order to get the full IPv6 API, including inet_ntop(), and we +# need to specify it as 0x0601 ("NT 6.1", a/k/a Windows 7) or higher +# in order to get NdisMediumIP. +# +# NOTE: pcap does *NOT* work with msvcrt.dll; it must link with +# a newer version of the C library, i.e. Visual Studio 2015 or +# later, as it depends on C99 features introduced in VS 2015. +# +if(MINGW) + add_definitions(-D_WIN32_WINNT=0x0601) +endif(MINGW) # # Build all runtimes in the top-level binary directory; that way, @@ -106,10 +459,14 @@ if(WIN32) option(USE_STATIC_RT "Use static Runtime" ON) endif(WIN32) option(BUILD_SHARED_LIBS "Build shared libraries" ON) +set(dpdk_ROOT "" CACHE PATH "Path to directory with include and lib subdirectories for DPDK") if(WIN32) - set(PACKET_DLL_DIR "" CACHE PATH "Path to directory with include and lib subdirectories for packet.dll") + set(Packet_ROOT "" CACHE PATH "Path to directory with include and lib subdirectories for packet.dll") + set(AirPcap_ROOT "" CACHE PATH "Path to directory with include and lib subdirectories for airpcap.dll") endif(WIN32) +option(ENABLE_PROFILING "Enable code profiling" OFF) + # To pacify those who hate the protochain instruction option(NO_PROTOCHAIN "Disable protochain instruction" OFF) @@ -131,16 +488,19 @@ else() endif(WIN32) if(CMAKE_SYSTEM_NAME STREQUAL "Linux") - option(PCAP_SUPPORT_PACKET_RING "Enable Linux packet ring support" ON) option(BUILD_WITH_LIBNL "Build with libnl" ON) endif() # # Additional capture modules. # -option(DISABLE_USB "Disable USB sniffing support" OFF) +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + option(DISABLE_LINUX_USBMON "Disable Linux usbmon USB sniffing support" OFF) +endif() option(DISABLE_BLUETOOTH "Disable Bluetooth sniffing support" OFF) option(DISABLE_NETMAP "Disable netmap support" OFF) +option(DISABLE_DPDK "Disable DPDK support" OFF) + # # We don't support D-Bus sniffing on macOS; see # @@ -221,25 +581,26 @@ if(WIN32) endif(IS_DIRECTORY ${CMAKE_HOME_DIRECTORY}/../../Common) find_package(Packet) - if(PACKET_FOUND) + if(Packet_FOUND) set(HAVE_PACKET32 TRUE) - include_directories(${PACKET_INCLUDE_DIRS}) + include_directories(${Packet_INCLUDE_DIRS}) # # Check whether we have the NPcap PacketIsLoopbackAdapter() # function. # cmake_push_check_state() - set(CMAKE_REQUIRED_LIBRARIES ${PACKET_LIBRARIES}) + set(CMAKE_REQUIRED_LIBRARIES ${Packet_LIBRARIES}) check_function_exists(PacketIsLoopbackAdapter HAVE_PACKET_IS_LOOPBACK_ADAPTER) + check_function_exists(PacketGetTimestampModes HAVE_PACKET_GET_TIMESTAMP_MODES) cmake_pop_check_state() - endif(PACKET_FOUND) + endif(Packet_FOUND) message(STATUS "checking for Npcap's version.h") - check_symbol_exists(WINPCAP_PRODUCT_NAME "../../version.h" HAVE_VERSION_H) + check_symbol_exists(WINPCAP_PRODUCT_NAME "${CMAKE_SOURCE_DIR}/../../version.h" HAVE_VERSION_H) if(HAVE_VERSION_H) - message(STATUS "HAVE version.h") + message(STATUS "HAVE version.h") else(HAVE_VERSION_H) - message(STATUS "MISSING version.h") + message(STATUS "MISSING version.h") endif(HAVE_VERSION_H) endif(WIN32) @@ -294,42 +655,50 @@ if(NOT WIN32) check_include_file(sys/ioccom.h HAVE_SYS_IOCCOM_H) check_include_file(sys/sockio.h HAVE_SYS_SOCKIO_H) check_include_file(sys/select.h HAVE_SYS_SELECT_H) -endif(NOT WIN32) -check_include_file(limits.h HAVE_LIMITS_H) -if(NOT WIN32) - check_include_file(netpacket/packet.h HAVE_NETPACKET_PACKET_H) - check_include_files("sys/types.h;sys/socket.h;net/if.h;net/pfvar.h" HAVE_NET_PFVAR_H) - if(HAVE_NET_PFVAR_H) - # - # Check for various PF actions. - # - check_c_source_compiles( -"#include -#include -#include -#include -int -main(void) -{ - return PF_NAT+PF_NONAT+PF_BINAT+PF_NOBINAT+PF_RDR+PF_NORDR; -} -" - HAVE_PF_NAT_THROUGH_PF_NORDR) - endif(HAVE_NET_PFVAR_H) + check_include_file(netpacket/packet.h HAVE_NETPACKET_PACKET_H) check_include_file(netinet/if_ether.h HAVE_NETINET_IF_ETHER_H) - if(CMAKE_SYSTEM_NAME STREQUAL "Linux") - check_include_file(linux/sockios.h HAVE_LINUX_SOCKIOS_H) - # - # linux/if_bonding.h requires sys/socket.h. - # - check_include_files("sys/socket.h;linux/if_bonding.h" HAVE_LINUX_IF_BONDING_H) - endif() endif(NOT WIN32) # # Functions. # +# First, check for the __atomic_load_n() and __atomic_store_n() +# builtins. +# +# We can't use check_function_exists(), as it tries to declare +# the function, and attempting to declare a compiler builtin +# can produce an error. +# +# We don't use check_symbol_exists(), as it expects a header +# file to be specified to declare the function, but there isn't +# such a header file. +# +# So we use check_c_source_compiles(). +# +check_c_source_compiles( +"int +main(void) +{ + int i = 17; + return __atomic_load_n(&i, __ATOMIC_RELAXED); +} +" + HAVE___ATOMIC_LOAD_N) +check_c_source_compiles( +"int +main(void) +{ + int i; + __atomic_store_n(&i, 17, __ATOMIC_RELAXED); + return 0; +} +" + HAVE___ATOMIC_STORE_N) + +# +# Now check for various system functions. +# check_function_exists(strerror HAVE_STRERROR) check_function_exists(strerror_r HAVE_STRERROR_R) if(HAVE_STRERROR_R) @@ -356,14 +725,28 @@ main(void) endif(NOT HAVE_GNU_STRERROR_R) else(HAVE_STRERROR_R) # - # We don't have strerror_r; do we have strerror_s? + # We don't have strerror_r; do we have _wcserror_s? # - check_function_exists(strerror_s HAVE_STRERROR_S) + check_function_exists(_wcserror_s HAVE__WCSERROR_S) endif(HAVE_STRERROR_R) + +# +# Make sure we have vsnprintf() and snprintf(); we require them. +# We use check_symbol_exists(), as they aren't necessarily external +# functions - in Visual Studio, for example, they're inline functions +# calling a common external function. +# +check_symbol_exists(vsnprintf "stdio.h" HAVE_VSNPRINTF) +if(NOT HAVE_VSNPRINTF) + message(FATAL_ERROR "vsnprintf() is required but wasn't found") +endif(NOT HAVE_VSNPRINTF) +check_symbol_exists(snprintf "stdio.h" HAVE_SNPRINTF) +if(NOT HAVE_SNPRINTF) + message(FATAL_ERROR "snprintf() is required but wasn't found") +endif() + check_function_exists(strlcpy HAVE_STRLCPY) check_function_exists(strlcat HAVE_STRLCAT) -check_function_exists(snprintf HAVE_SNPRINTF) -check_function_exists(vsnprintf HAVE_VSNPRINTF) check_function_exists(asprintf HAVE_ASPRINTF) check_function_exists(vasprintf HAVE_VASPRINTF) check_function_exists(strtok_r HAVE_STRTOK_R) @@ -397,6 +780,10 @@ endif() # that's been set, it skips the test, so we need different variables. # set(PCAP_LINK_LIBRARIES "") +set(LIBS "") +set(LIBS_STATIC "") +set(REQUIRES_PRIVATE "") +set(LIBS_PRIVATE "") include(CheckLibraryExists) if(WIN32) # @@ -432,11 +819,25 @@ else(WIN32) # OK, we found it in libsocket. # set(PCAP_LINK_LIBRARIES socket nsl ${PCAP_LINK_LIBRARIES}) + set(LIBS "-lsocket -lnsl ${LIBS}") + set(LIBS_STATIC "-lsocket -lnsl ${LIBS_STATIC}") + set(LIBS_PRIVATE "-lsocket -lnsl ${LIBS_PRIVATE}") else(LIBSOCKET_HAS_GETADDRINFO) - # - # We didn't find it. - # - message(FATAL_ERROR "getaddrinfo is required, but wasn't found") + check_library_exists(network getaddrinfo "" LIBNETWORK_HAS_GETADDRINFO) + if(LIBNETWORK_HAS_GETADDRINFO) + # + # OK, we found it in libnetwork (Haiku). + # + set(PCAP_LINK_LIBRARIES network ${PCAP_LINK_LIBRARIES}) + set(LIBS "-lnetwork ${LIBS}") + set(LIBS_STATIC "-lnetwork ${LIBS_STATIC}") + set(LIBS_PRIVATE "-lnetwork ${LIBS_PRIVATE}") + else(LIBNETWORK_HAS_GETADDRINFO) + # + # We didn't find it. + # + message(FATAL_ERROR "getaddrinfo is required, but wasn't found") + endif(LIBNETWORK_HAS_GETADDRINFO) endif(LIBSOCKET_HAS_GETADDRINFO) # @@ -452,6 +853,9 @@ else(WIN32) # Yes - link with it as well. # set(PCAP_LINK_LIBRARIES xnet ${PCAP_LINK_LIBRARIES}) + set(LIBSC "-lxnet ${LIBS_LIBS}") + set(LIBS_STATIC "-lxnet ${LIBS_STATIC}") + set(LIBS_PRIVATE "-lxnet ${LIBS_PRIVATE}") endif(LIBXNET_HAS_RECVMSG) endif(NOT STDLIBS_HAVE_GETADDRINFO) @@ -461,8 +865,20 @@ else(WIN32) check_library_exists(str putmsg "" LIBSTR_HAS_PUTMSG) if(LIBSTR_HAS_PUTMSG) set(PCAP_LINK_LIBRARIES str ${PCAP_LINK_LIBRARIES}) + set(LIBS "-lstr ${LIBS}") + set(LIBS_STATIC "-lstr ${LIBS_STATIC}") + set(LIBS_PRIVATE "-lstr ${LIBS_PRIVATE}") endif(LIBSTR_HAS_PUTMSG) endif(NOT STDLIBS_HAVE_PUTMSG) + + # Haiku has getpass in libbsd + check_function_exists(getpass STDLIBS_HAVE_GETPASS) + if(NOT STDLIBS_HAVE_GETPASS) + check_library_exists(bsd getpass "" LIBBSD_HAS_GETPASS) + if(LIBBSD_HAS_GETPASS) + set(PCAP_LINK_LIBRARIES bsd ${PCAP_LINK_LIBRARIES}) + endif(LIBBSD_HAS_GETPASS) + endif(NOT STDLIBS_HAVE_GETPASS) endif(WIN32) # @@ -812,6 +1228,195 @@ if(NOT WIN32) endif(NOT CMAKE_USE_PTHREADS_INIT) endif(NOT WIN32) +if(ENABLE_PROFILING) + if(NOT MSVC) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pg") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg") + endif() +endif() + +# +# Based on +# +# https://github.com/commonmark/cmark/blob/master/FindAsan.cmake +# +# The MIT License (MIT) +# +# Copyright (c) 2013 Matthew Arsenault +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# Test if the each of the sanitizers in the ENABLE_SANITIZERS list are +# supported by the compiler, and, if so, adds the appropriate flags to +# CMAKE_C_FLAGS, CMAKE_CXX_FLAGS, and SANITIZER_FLAGS. If not, it fails. +# +# Do this last, in the hope that it will prevent configuration on Linux +# from somehow deciding it doesn't need -lpthread when building rpcapd +# (it does require it, but somehow, in some mysterious fashion that no +# obvious CMake debugging flag reveals, it doesn't realize that if we +# turn sanitizer stuff on). +# +set(SANITIZER_FLAGS "") +foreach(sanitizer IN LISTS ENABLE_SANITIZERS) + # Set -Werror to catch "argument unused during compilation" warnings + + message(STATUS "Checking sanitizer ${sanitizer}") + set(sanitizer_variable "sanitize_${sanitizer}") + set(CMAKE_REQUIRED_FLAGS "-Werror -fsanitize=${sanitizer}") + check_c_compiler_flag("-fsanitize=${sanitizer}" ${sanitizer_variable}) + if(${${sanitizer_variable}}) + set(SANITIZER_FLAGS "${SANITIZER_FLAGS} -fsanitize=${sanitizer}") + message(STATUS "${sanitizer} sanitizer supported using -fsanitizer=${sanitizer}") + else() + # + # Try the versions supported prior to Clang 3.2. + # If the sanitizer is "address", try -fsanitize-address. + # If it's "undefined", try -fcatch-undefined-behavior. + # Otherwise, give up. + # + set(sanitizer_variable "OLD_${sanitizer_variable}") + if ("${sanitizer}" STREQUAL "address") + set(CMAKE_REQUIRED_FLAGS "-Werror -fsanitize-address") + check_c_compiler_flag("-fsanitize-address" ${sanitizer_variable}) + if(${${sanitizer_variable}}) + set(SANITIZER_FLAGS "${SANITIZER_FLAGS} -fsanitize-address") + message(STATUS "${sanitizer} sanitizer supported using -fsanitize-address") + else() + message(FATAL_ERROR "${sanitizer} isn't a supported sanitizer") + endif() + elseif("${sanitizer}" STREQUAL "undefined") + set(CMAKE_REQUIRED_FLAGS "-Werror -fcatch-undefined-behavior") + check_c_compiler_flag("-fcatch-undefined-behavior" ${sanitizer_variable}) + if(${${sanitizer_variable}}) + set(SANITIZER_FLAGS "${SANITIZER_FLAGS} -fcatch-undefined-behavior") + message(STATUS "${sanitizer} sanitizer supported using catch-undefined-behavior") + else() + message(FATAL_ERROR "${sanitizer} isn't a supported sanitizer") + endif() + else() + message(FATAL_ERROR "${sanitizer} isn't a supported sanitizer") + endif() + endif() + + unset(CMAKE_REQUIRED_FLAGS) +endforeach() + +if(NOT "${SANITIZER_FLAGS}" STREQUAL "") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O1 -g ${SANITIZER_FLAGS} -fno-omit-frame-pointer -fno-optimize-sibling-calls") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O1 -g ${SANITIZER_FLAGS} -fno-omit-frame-pointer -fno-optimize-sibling-calls") +endif() + +# +# OpenSSL/libressl. +# +find_package(OpenSSL) +if(OPENSSL_FOUND) + # + # We have OpenSSL. + # + include_directories(SYSTEM ${OPENSSL_INCLUDE_DIR}) + set(PCAP_LINK_LIBRARIES ${PCAP_LINK_LIBRARIES} ${OPENSSL_LIBRARIES}) + + # + # The find_package() module CMake provides for OpenSSL uses does not + # give us a defined indication of whether it found OpenSSL with + # pkg-config or not. We need to know that as, if it was found with + # pkg-config, we should set the Requires.private value in libpcap.pc + # to include its package name, openssl, otherwise we should add the + # names for the static libraries to Libs.private. + # + # On UN*X, FindOpenSSL happens to use pkg-config to find OpenSSL, but + # it doesn't appear to be documented as doing so; therefore, we don't + # assume that, if we got here, we have pkg-config. + # + # So we use pkg_get_link_info() to run pkg-config ourselves, both + # because FindOpenSSL doesn't set the OPENSSL_LDFLAGS or + # OPENSSL_STATIC_LDFLAGS variables and because, for reasons explained + # in the comment before the pkg_get_link_info() macro, even if it did, + # it wouldn't be what we want anyway. + # + if (PKG_CONFIG_EXECUTABLE) + pkg_get_link_info(OPENSSL openssl) + if (OPENSSL_FOUND_WITH_PKG_CONFIG) + # + # pkg-config failed; assume that means that there is no openssl + # package for it to find. Just add OPENSSL_LIBRARIES to + # LIBS_PRIVATE AND LIBS_STATIC, as that's the + # best we can do. XXX - need list of -l and -L flags to add.... + # + set(LIBS "${LIBS} ${OPENSSL_LIBS}") + set(LIBS_STATIC "${LIBS_STATIC} ${OPENSSL_LIBS_STATIC}") + set(REQUIRES_PRIVATE "${REQUIRES_PRIVATE} ${OPENSSL_PACKAGE_NAME}") + endif() + else() + # Get it from OPENSSL_LIBRARIES + foreach(_lib IN LISTS OPENSSL_LIBRARIES) + # + # Get the directory in which the library resides. + # + get_filename_component(_lib_directory "${_lib}" DIRECTORY) + + # + # Is the library directory in CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES? + # (See comment above on why we use that.) + # + list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${_lib_directory}" _lib_index) + if(_lib_index EQUAL -1) + # + # No, so add a -L flag to get the linker to search in that + # directory. + # + set(LIBS "${LIBS} -L${_lib_directory}") + set(LIBS_STATIC "${LIBS_STATIC} -L${_lib_directory}") + set(LIBS_PRIVATE "${LIBS_PRIVATE} -L${_lib_directory}") + endif() + + # + # Get the file name of the library, without the extension. + # + get_filename_component(_lib_filename "${_lib}" NAME_WE) + + # + # Strip off the "lib" prefix to get the library name, and + # add a -l flag based on that. + # + string(REGEX REPLACE "^lib" "" _library_name "${_lib_filename}") + set(LIBS "${LIBS} -l${_library_name}") + set(LIBS_STATIC "${LIBS_STATIC} -l${_library_name}") + set(LIBS_PRIVATE "${LIBS_PRIVATE} -l${_library_name}") + endforeach() + endif() + set(HAVE_OPENSSL YES) +endif(OPENSSL_FOUND) + +# +# Additional linker flags. +# +set(LINKER_FLAGS "${SANITIZER_FLAGS}") +if(ENABLE_PROFILING) + if(MSVC) + set(LINKER_FLAGS " /PROFILE") + else() + set(LINKER_FLAGS " -pg") + endif() +endif() + ###################################### # Input files ###################################### @@ -826,6 +1431,8 @@ set(PROJECT_SOURCE_LIST_C nametoaddr.c optimize.c pcap-common.c + pcap-usb-linux-common.c + pcap-util.c pcap.c savefile.c sf-pcapng.c @@ -834,52 +1441,16 @@ set(PROJECT_SOURCE_LIST_C if(WIN32) # - # For now, we assume we don't have snprintf() or that it's not one - # that behaves enough like C99's snprintf() for our purposes (i.e., - # it doesn't null-terminate the string if it truncates it to fit in - # the buffer), so we have to provide our own (a wrapper around - # _snprintf() that null-terminates the buffer). + # We add the character set conversion routines; they're Windows-only + # for now. # - # We also assume we don't have asprintf(), and provide an implementation + # We assume we don't have asprintf(), and provide an implementation # that uses _vscprintf() to determine how big the string needs to be. # set(PROJECT_SOURCE_LIST_C ${PROJECT_SOURCE_LIST_C} - missing/win_snprintf.c missing/win_asprintf.c) + charconv.c missing/win_asprintf.c) else() - # - # Either: - # - # we have snprintf() and vsnprintf(), and have asprintf() and - # vasprintf(); - # - # we have snprintf() and vsnprintf(), but don't have asprintf() - # or vasprintf(); - # - # we have neither snprintf() nor vsnprintf(), and don't have - # asprintf() or vasprintf(), either. - # - # We assume that if we have asprintf() we have vasprintf(), as well - # as snprintf() and vsnprintf(), and that if we have snprintf() we - # have vsnprintf(). - # - # For the first case, we don't need any replacement routines. - # For the second case, we need replacement asprintf()/vasprintf() - # routines. - # For the third case, we need replacement snprintf()/vsnprintf() and - # asprintf()/vasprintf() routines. - # - if(NOT HAVE_SNPRINTF) - # - # We assume we have none of them; missing/snprintf.c supplies - # all of them. - # - set(PROJECT_SOURCE_LIST_C ${PROJECT_SOURCE_LIST_C} missing/snprintf.c) - elif(NOT HAVE_ASPRINTF) - # - # We assume we have snprintf()/vsnprintf() but lack - # asprintf()/vasprintf(); missing/asprintf.c supplies - # the latter (using vsnprintf()). - # + if(NOT HAVE_ASPRINTF) set(PROJECT_SOURCE_LIST_C ${PROJECT_SOURCE_LIST_C} missing/asprintf.c) endif() if(NOT HAVE_STRLCAT) @@ -985,6 +1556,7 @@ else() check_include_file(linux/socket.h HAVE_LINUX_SOCKET_H) check_include_file(net/raw.h HAVE_NET_RAW_H) check_include_file(sys/dlpi.h HAVE_SYS_DLPI_H) + check_include_file(config/HaikuConfig.h HAVE_CONFIG_HAIKUCONFIG_H) if(BPF_H_DEFINES_BIOCSETIF) # @@ -1028,28 +1600,38 @@ else() # DLPI on pre-Solaris 11 SunOS 5, HP-UX, possibly others. # set(PCAP_TYPE dlpi) + elseif(HAVE_CONFIG_HAIKUCONFIG_H) + # + # Haiku. + # + set(PCAP_TYPE haiku) else() # # Nothing we support. # set(PCAP_TYPE null) + message(WARNING +"cannot determine packet capture interface +(see the INSTALL.md file for more info)") endif() endif() endif(WIN32) message(STATUS "Packet capture mechanism type: ${PCAP_TYPE}") +find_package(PkgConfig QUIET) + # # Do capture-mechanism-dependent tests. # if(WIN32) if(PCAP_TYPE STREQUAL "npf") # - # Link with packet.dll before WinSock2. + # Link with packet.dll before Winsock2. # - set(PCAP_LINK_LIBRARIES ${PACKET_LIBRARIES} ${PCAP_LINK_LIBRARIES}) + set(PCAP_LINK_LIBRARIES ${Packet_LIBRARIES} ${PCAP_LINK_LIBRARIES}) elseif(PCAP_TYPE STREQUAL "null") else() - message(ERROR "${PCAP_TYPE} is not a valid pcap type") + message(FATAL_ERROR "${PCAP_TYPE} is not a valid pcap type") endif() else(WIN32) if(PCAP_TYPE STREQUAL "dlpi") @@ -1085,6 +1667,9 @@ else(WIN32) # XXX - add -L/lib # set(PCAP_LINK_LIBRARIES ${PCAP_LINK_LIBRARIES} dlpi) + set(LIBS "${LIBS} -ldlpi") + set(LIBS_STATIC "${LIBS_STATIC} -ldlpi") + set(LIBS_PRIVATE "${LIBS_PRIVATE} -ldlpi") set(PCAP_TYPE libdlpi) endif() @@ -1109,70 +1694,45 @@ else(WIN32) # # Do we have libnl? + # We only want version 3. Version 2 was, apparently, + # short-lived, and version 1 is source and binary + # incompatible with version 3, and it appears that, + # these days, everybody's using version 3. We're + # not supporting older versions of the Linux kernel; + # let's drop support for older versions of libnl, too. # if(BUILD_WITH_LIBNL) - # - # Try libnl 3.x first. - # - cmake_push_check_state() - set(CMAKE_REQUIRED_LIBRARIES nl-3) - check_function_exists(nl_socket_alloc HAVE_LIBNL) - cmake_pop_check_state() - if(HAVE_LIBNL) + pkg_check_modules(LIBNL libnl-genl-3.0) + if(LIBNL_FOUND) + set(PCAP_LINK_LIBRARIES ${LIBNL_LIBRARIES} ${PCAP_LINK_LIBRARIES}) + # - # Yes, we have libnl 3.x. + # Get raw link flags from pkg-config. # - set(PCAP_LINK_LIBRARIES nl-genl-3 nl-3 ${PCAP_LINK_LIBRARIES}) - set(HAVE_LIBNL_3_x ON) - set(HAVE_LIBNL_NLE ON) - set(HAVE_LIBNL_SOCKETS ON) - include_directories("/usr/include/libnl3") + pkg_get_link_info(LIBNL libnl-genl-3.0) + set(LIBS "${LIBNL_LIBS} ${LIBS}") + set(LIBS_STATIC "${LIBNL_LIBS_STATIC} ${LIBS_STATIC}") + set(REQUIRES_PRIVATE "${LIBNL_PACKAGE_NAME} ${REQUIRES_PRIVATE}") else() - # - # Try libnl 2.x. - # cmake_push_check_state() - set(CMAKE_REQUIRED_LIBRARIES nl) + set(CMAKE_REQUIRED_LIBRARIES nl-3) check_function_exists(nl_socket_alloc HAVE_LIBNL) cmake_pop_check_state() if(HAVE_LIBNL) # - # Yes, we have libnl 2.x. + # Yes, we have libnl 3.x. # - set(PCAP_LINK_LIBRARIES nl-genl nl ${PCAP_LINK_LIBRARIES}) - set(HAVE_LIBNL_2_x ON) - set(HAVE_LIBNL_NLE ON) - set(HAVE_LIBNL_SOCKETS ON) - else() - # - # No, we don't; do we have libnl 1.x? - # - cmake_push_check_state() - set(CMAKE_REQUIRED_LIBRARIES nl) - check_function_exists(nl_handle_alloc HAVE_LIBNL) - cmake_pop_check_state() - if(HAVE_LIBNL) - set(PCAP_LINK_LIBRARIES nl ${PCAP_LINK_LIBRARIES}) - endif() + set(PCAP_LINK_LIBRARIES nl-genl-3 nl-3 ${PCAP_LINK_LIBRARIES}) + include_directories("/usr/include/libnl3") + set(LIBS "-lnl-genl-3 -lnl-3 ${LIBS}") + set(LIBS_STATIC "-lnl-genl-3 -lnl-3 ${LIBS_STATIC}") + set(LIBS_PRIVATE "-lnl-genl-3 -lnl-3 ${LIBS_PRIVATE}") endif() endif() + else() + unset(HAVE_LIBNL CACHE) # check_function_exists stores results in cache endif() - check_include_file(linux/ethtool.h HAVE_LINUX_ETHTOOL_H) - - # - # Checks to see if tpacket_stats is defined in linux/if_packet.h - # If so then pcap-linux.c can use this to report proper statistics. - # - # XXX - there's no check_type() macro that's like check_type_size() - # except that it only checks for the existence of the structure type, - # so we use check_type_size() and ignore the size. - # - cmake_push_check_state() - set(CMAKE_EXTRA_INCLUDE_FILES linux/if_packet.h) - check_type_size("struct tpacket_stats" STRUCT_TPACKET_STATS) - cmake_pop_check_state() - check_struct_has_member("struct tpacket_auxdata" tp_vlan_tci linux/if_packet.h HAVE_STRUCT_TPACKET_AUXDATA_TP_VLAN_TCI) elseif(PCAP_TYPE STREQUAL "bpf") # @@ -1196,13 +1756,23 @@ else(WIN32) check_type_size("struct BPF_TIMEVAL" STRUCT_BPF_TIMEVAL) endif() cmake_pop_check_state() + elseif(PCAP_TYPE STREQUAL "haiku") + # + # Check for some headers just in case. + # + check_include_files("net/if.h;net/if_dl.h;net/if_types.h" HAVE_NET_IF_TYPES_H) + set(PCAP_SRC pcap-${PCAP_TYPE}.cpp) elseif(PCAP_TYPE STREQUAL "null") else() message(FATAL_ERROR "${PCAP_TYPE} is not a valid pcap type") endif() endif(WIN32) -set(PROJECT_SOURCE_LIST_C ${PROJECT_SOURCE_LIST_C} pcap-${PCAP_TYPE}.c) +if(NOT DEFINED PCAP_SRC) +set(PCAP_SRC pcap-${PCAP_TYPE}.c) +endif() + +set(PROJECT_SOURCE_LIST_C ${PROJECT_SOURCE_LIST_C} ${PCAP_SRC}) # # Now figure out how we get a list of interfaces and addresses, @@ -1239,6 +1809,9 @@ if(NOT WIN32) check_library_exists(socket getifaddrs "" SOCKET_HAS_GETIFADDRS) if(SOCKET_HAS_GETIFADDRS) set(PCAP_LINK_LIBRARIES socket ${PCAP_LINK_LIBRARIES}) + set(LIBS "-lsocket ${LIBS}") + set(LIBS_STATIC "-lsocket ${LIBS_STATIC}") + set(LIBS_PRIVATE "-lsocket ${LIBS_PRIVATE}") set(HAVE_GETIFADDRS TRUE) endif() endif() @@ -1308,13 +1881,14 @@ endif() # Check for additional native sniffing capabilities. # -# Check for USB sniffing support on Linux. -# On FreeBSD, it uses BPF, so we don't need to do anything special here. -if(NOT DISABLE_USB) - if(CMAKE_SYSTEM_NAME STREQUAL "Linux") - set(PCAP_SUPPORT_USB TRUE) +# +# Various Linux-specific mechanisms. +# +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + # Check for usbmon USB sniffing support. + if(NOT DISABLE_LINUX_USBMON) + set(PCAP_SUPPORT_LINUX_USBMON TRUE) set(PROJECT_SOURCE_LIST_C ${PROJECT_SOURCE_LIST_C} pcap-usb-linux.c) - set(LINUX_USB_MON_DEV /dev/usbmon) # # Do we have a version of available? # If so, we might need it for . @@ -1342,10 +1916,9 @@ if(NOT DISABLE_USB) endif(HAVE_LINUX_COMPILER_H) endif() endif() -endif() -# Check for netfilter sniffing support. -if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + # + # Check for netfilter sniffing support. # # Life's too short to deal with trying to get this to compile # if you don't get the right types defined with @@ -1401,6 +1974,51 @@ main(void) endif(PCAP_SUPPORT_NETMAP) endif() +# Check for DPDK sniffing support +if(NOT DISABLE_DPDK) + find_package(dpdk) + if(dpdk_FOUND) + # + # We call rte_eth_dev_count_avail(), and older versions of DPDK + # didn't have it, so check for it. + # + cmake_push_check_state() + set(CMAKE_REQUIRED_INCLUDES ${dpdk_INCLUDE_DIRS}) + set(CMAKE_REQUIRED_LIBRARIES ${dpdk_LIBRARIES}) + check_function_exists(rte_eth_dev_count_avail HAVE_RTE_ETH_DEV_COUNT_AVAIL) + cmake_pop_check_state() + if(HAVE_RTE_ETH_DEV_COUNT_AVAIL) + set(DPDK_C_FLAGS "-march=native") + set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} ${DPDK_C_FLAGS}) + include_directories(AFTER ${dpdk_INCLUDE_DIRS}) + link_directories(AFTER ${dpdk_LIBRARIES}) + set(PCAP_LINK_LIBRARIES ${PCAP_LINK_LIBRARIES} ${dpdk_LIBRARIES}) + set(LIBS "${LIBS} ${dpdk_LIBS}") + set(LIBS_STATIC "${LIBS_STATIC} ${dpdk_LIBS_STATIC}") + set(REQUIRES_PRIVATE "${REQUIRES_PRIVATE} ${dpdk_PACKAGE_NAME}") + set(PROJECT_SOURCE_LIST_C ${PROJECT_SOURCE_LIST_C} pcap-dpdk.c) + set(PCAP_SUPPORT_DPDK TRUE) + + # + # Check whether the rte_ether.h file defines + # struct ether_addr or struct rte_ether_addr. + # + # ("API compatibility? That's for losers!") + # + cmake_push_check_state() + set(CMAKE_REQUIRED_INCLUDES ${dpdk_INCLUDE_DIRS}) + set(CMAKE_EXTRA_INCLUDE_FILES rte_ether.h) + check_type_size("struct rte_ether_addr" STRUCT_RTE_ETHER_ADDR) + cmake_pop_check_state() + endif() + else() + message(WARNING, +"We couldn't find DPDK with pkg-config. If you want DPDK support, +make sure that pkg-config is installed, that DPDK 18.02.2 or later is +installed, and that DPDK provides a .pc file.") + endif() +endif() + # Check for Bluetooth sniffing support if(NOT DISABLE_BLUETOOTH) if(CMAKE_SYSTEM_NAME STREQUAL "Linux") @@ -1439,9 +2057,11 @@ main(void) endif(HAVE_STRUCT_SOCKADDR_HCI_HCI_CHANNEL) endif(HAVE_BLUETOOTH_BLUETOOTH_H) endif() +else() + unset(PCAP_SUPPORT_BT_MONITOR CACHE) endif() -# Check for Bluetooth sniffing support +# Check for D-Bus sniffing support if(NOT DISABLE_DBUS) # # We don't support D-Bus sniffing on macOS; see @@ -1451,7 +2071,6 @@ if(NOT DISABLE_DBUS) if(APPLE) message(FATAL_ERROR "Due to freedesktop.org bug 74029, D-Bus capture support is not available on macOS") endif(APPLE) - include(FindPkgConfig) pkg_check_modules(DBUS dbus-1) if(DBUS_FOUND) set(PCAP_SUPPORT_DBUS TRUE) @@ -1479,22 +2098,80 @@ if(NOT DISABLE_DBUS) list(APPEND DBUS_LIBRARY_FULLPATHS ${_libfullpath}) endforeach() set(PCAP_LINK_LIBRARIES ${PCAP_LINK_LIBRARIES} ${DBUS_LIBRARY_FULLPATHS}) + + # + # Get library information for DPDK. + # + pkg_get_link_info(DBUS dbus-1) + set(LIBS "${LIBS} ${DBUS_LIBS}") + set(LIBS_STATIC "${LIBS_STATIC} ${DBUS_LIBS_STATIC}") + set(REQUIRES_PRIVATE "${REQUIRES_PRIVATE} ${DBUS_PACKAGE_NAME}") endif(DBUS_FOUND) endif(NOT DISABLE_DBUS) # Check for RDMA sniffing support if(NOT DISABLE_RDMA) - check_library_exists(ibverbs ibv_get_device_list "" LIBIBVERBS_HAS_IBV_GET_DEVICE_LIST) - if(LIBIBVERBS_HAS_IBV_GET_DEVICE_LIST) + pkg_check_modules(LIBIBVERBS libibverbs) + if(LIBIBVERBS_FOUND) + # + # pkg-config found it; remember its pkg-config name. + # + set(LIBIBVERBS_REQUIRES_PRIVATE ${LIBIBVERBS_PACKAGE_NAME}) + + # + # Get static linking information for it. + # + pkg_get_link_info(LIBIBVERBS libibverbs) + else() + # + # pkg-config didn't find it; try to look for it ourselves + # + check_library_exists(ibverbs ibv_get_device_list "" LIBIBVERBS_HAS_IBV_GET_DEVICE_LIST) + if(LIBIBVERBS_HAS_IBV_GET_DEVICE_LIST) + set(LIBIBVERBS_FOUND TRUE) + set(LIBIBVERBS_LIBRARIES ibverbs) + # XXX - at least on Ubuntu 20.04, there are many more + # libraries needed; is there any platform where + # libibverbs is available but where pkg-config + # isn't available or libibverbs doesn't use it? + # If not, we should only use pkg-config for it. + set(LIBIBVERBS_STATIC_LIBRARIES ibverbs) + set(LIBIBVERBS_LIBS -libverbs) + set(LIBIBVERBS_LIBS_STATIC -libverbs) + set(LIBIBVERBS_LIBS_PRIVATE -libverbs) + endif() + endif() + if(LIBIBVERBS_FOUND) + # + # For unknown reasons, check_include_file() doesn't just attempt + # to compile a test program that includes the header in + # question, it also attempts to link it. + # + # For unknown reasons, at least some of the static inline + # functions defined in infiniband/verbs.h are not inlined by the + # Sun^WOracle Studio C compiler, so the compiler generates code + # for them as part of the object code resulting from compiling + # the test program. At lest some of those functions call + # routines in -libverbs, so, in order to keep the compile and + # link from failing, even though the header file exists and is + # usable, we need to link with -libverbs. + # + cmake_push_check_state() + set(CMAKE_REQUIRED_LIBRARIES ${LIBIBVERBS_LIBRARIES}) check_include_file(infiniband/verbs.h HAVE_INFINIBAND_VERBS_H) if(HAVE_INFINIBAND_VERBS_H) check_symbol_exists(ibv_create_flow infiniband/verbs.h PCAP_SUPPORT_RDMASNIFF) if(PCAP_SUPPORT_RDMASNIFF) set(PROJECT_SOURCE_LIST_C ${PROJECT_SOURCE_LIST_C} pcap-rdmasniff.c) - set(PCAP_LINK_LIBRARIES ibverbs ${PCAP_LINK_LIBRARIES}) + set(PCAP_LINK_LIBRARIES ${LIBIBVERBS_LIBRARIES} ${PCAP_LINK_LIBRARIES}) + set(LIBS "${LIBIBVERBS_LIBS} ${LIBS}") + set(LIBS_STATIC "${LIBIBVERBS_LIBS_STATIC} ${LIBS_STATIC}") + set(LIBS_PRIVATE "${LIBIBVERBS_LIBS_PRIVATE} ${LIBS_PRIVATE}") + set(REQUIRES_PRIVATE "${REQUIRES_PRIVATE} ${LIBIBVERBS_PACKAGE_NAME}") endif(PCAP_SUPPORT_RDMASNIFF) endif(HAVE_INFINIBAND_VERBS_H) - endif(LIBIBVERBS_HAS_IBV_GET_DEVICE_LIST) + cmake_pop_check_state() + endif(LIBIBVERBS_FOUND) endif(NOT DISABLE_RDMA) # @@ -1532,12 +2209,18 @@ if(NOT DISABLE_DAG) set(PROJECT_SOURCE_LIST_C ${PROJECT_SOURCE_LIST_C} pcap-dag.c) set(HAVE_DAG_API TRUE) set(PCAP_LINK_LIBRARIES ${PCAP_LINK_LIBRARIES} ${DAG_LIBRARIES}) + set(LIBS "${LIBS} ${DAG_LIBS}") + set(LIBS_STATIC "${LIBS_STATIC} ${DAG_LIBS_STATIC}") + set(LIBS_PRIVATE "${LIBS_PRIVATE} ${DAG_LIBS_PRIVATE}") if(HAVE_DAG_LARGE_STREAMS_API) get_filename_component(DAG_LIBRARY_DIR ${DAG_LIBRARY} PATH) check_library_exists(vdag vdag_set_device_info ${DAG_LIBRARY_DIR} HAVE_DAG_VDAG) if(HAVE_DAG_VDAG) set(PCAP_LINK_LIBRARIES ${PCAP_LINK_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) + set(LIBS "${LIBS} ${CMAKE_THREAD_LIBS_INIT}") + set(LIBS_STATIC "${LIBS_STATIC} ${CMAKE_THREAD_LIBS_INIT}") + set(LIBS_PRIVATE "${LIBS_PRIVATE} ${CMAKE_THREAD_LIBS_INIT}") endif() endif() endif() @@ -1583,6 +2266,30 @@ if(NOT DISABLE_SNF) set(PROJECT_SOURCE_LIST_C ${PROJECT_SOURCE_LIST_C} pcap-snf.c) set(HAVE_SNF_API TRUE) set(PCAP_LINK_LIBRARIES ${PCAP_LINK_LIBRARIES} ${SNF_LIBRARIES}) + set(LIBS "${LIBS_STATIC} ${SNF_LIBS}") + set(LIBS_STATIC "${LIBS_STATIC} ${SNF_LIBS_STATIC}") + set(LIBS_PRIVATE "${LIBS_PRIVATE} ${SNF_LIBS_PRIVATE}") + endif() +endif() + +# Check for Riverbed AirPcap support. +if(NOT DISABLE_AIRPCAP) + # + # Try to find the AirPcap header file and library. + # + find_package(AirPcap) + + # + # Did we succeed? + # + if(AirPcap_FOUND) + # + # Yes. + # + include_directories(AFTER ${AirPcap_INCLUDE_DIRS}) + set(PROJECT_SOURCE_LIST_C ${PROJECT_SOURCE_LIST_C} pcap-airpcap.c) + set(HAVE_AIRPCAP_API TRUE) + set(PCAP_LINK_LIBRARIES ${PCAP_LINK_LIBRARIES} ${AirPcap_LIBRARIES}) endif() endif() @@ -1603,7 +2310,7 @@ if(NOT DISABLE_TC) include_directories(AFTER ${TC_INCLUDE_DIRS}) set(PROJECT_SOURCE_LIST_C ${PROJECT_SOURCE_LIST_C} pcap-tc.c) set(HAVE_TC_API TRUE) - set(PCAP_LINK_LIBRARIES "${PCAP_LINK_LIBRARIES} ${TC_LIBRARIES} ${CMAKE_USE_PTHREADS_INIT} stdc++") + set(PCAP_LINK_LIBRARIES ${PCAP_LINK_LIBRARIES} ${TC_LIBRARIES} ${CMAKE_USE_PTHREADS_INIT} stdc++) endif() endif() @@ -1629,7 +2336,7 @@ if(ENABLE_REMOTE) check_struct_has_member("struct msghdr" msg_flags "ftmacros.h;sys/socket.h" HAVE_STRUCT_MSGHDR_MSG_FLAGS) cmake_pop_check_state() set(PROJECT_SOURCE_LIST_C ${PROJECT_SOURCE_LIST_C} - pcap-new.c pcap-rpcap.c rpcap-protocol.c sockutils.c) + pcap-new.c pcap-rpcap.c rpcap-protocol.c sockutils.c sslutils.c) endif(ENABLE_REMOTE) ################################################################### @@ -1698,6 +2405,20 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.devel OR EXISTS ${CMAKE_BINARY_DIR}/.deve # structure members. # check_and_add_compiler_option(-wd4820) + # + # We do *not* care about every single place the compiler would + # have inserted Spectre mitigation if only we had told it to + # do so with /Qspectre. Maybe it's worth it, as that's in + # Bison-generated code that we don't control. + # + # XXX - add /Qspectre if that is really worth doing. + # + check_and_add_compiler_option(-wd5045) + + # + # Treat all (remaining) warnings as errors. + # + check_and_add_compiler_option(-WX) else() # # Other compilers, including MSVC with a Clang front end and @@ -1705,21 +2426,23 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.devel OR EXISTS ${CMAKE_BINARY_DIR}/.deve # they might support GCC-style -W options. # check_and_add_compiler_option(-Wall) - check_and_add_compiler_option(-Wsign-compare) - check_and_add_compiler_option(-Wmissing-prototypes) - check_and_add_compiler_option(-Wstrict-prototypes) - check_and_add_compiler_option(-Wshadow) - check_and_add_compiler_option(-Wdeclaration-after-statement) - check_and_add_compiler_option(-Wused-but-marked-unused) - check_and_add_compiler_option(-Wdocumentation) check_and_add_compiler_option(-Wcomma) - check_and_add_compiler_option(-Wmissing-noreturn) # Warns about safeguards added in case the enums are extended # check_and_add_compiler_option(-Wcovered-switch-default) - check_and_add_compiler_option(-Wmissing-variable-declarations) - check_and_add_compiler_option(-Wunused-parameter) + check_and_add_compiler_option(-Wdocumentation) check_and_add_compiler_option(-Wformat-nonliteral) + check_and_add_compiler_option(-Wmissing-noreturn) + check_and_add_compiler_option(-Wmissing-prototypes) + check_and_add_compiler_option(-Wmissing-variable-declarations) + check_and_add_compiler_option(-Wpointer-arith) + check_and_add_compiler_option(-Wpointer-sign) + check_and_add_compiler_option(-Wshadow) + check_and_add_compiler_option(-Wsign-compare) + check_and_add_compiler_option(-Wshorten-64-to-32) + check_and_add_compiler_option(-Wstrict-prototypes) check_and_add_compiler_option(-Wunreachable-code) + check_and_add_compiler_option(-Wunused-parameter) + check_and_add_compiler_option(-Wused-but-marked-unused) endif() endif() @@ -1771,6 +2494,19 @@ if(NOT MSVC) endif() endif(NOT MSVC) +# +# Extra compiler options for the build matrix scripts to request -Werror or +# its equivalent if required. The CMake variable name cannot be CFLAGS +# because that is already used for a different purpose in CMake. Example +# usage: cmake -DEXTRA_CFLAGS='-Wall -Wextra -Werror' ... +# +if(NOT "${EXTRA_CFLAGS}" STREQUAL "") + foreach(_extra_cflag ${EXTRA_CFLAGS}) + check_and_add_compiler_option("${_extra_cflag}") + endforeach(_extra_cflag) + message(STATUS "Added extra compile options (${EXTRA_CFLAGS})") +endif() + # # Flex/Lex and YACC/Berkeley YACC/Bison. # From a mail message to the CMake mailing list by Andy Cedilnik of @@ -1786,6 +2522,28 @@ if(LEX_EXECUTABLE STREQUAL "LEX_EXECUTABLE-NOTFOUND") endif() message(STATUS "Lexical analyzer generator: ${LEX_EXECUTABLE}") +# +# Make sure {f}lex supports the -P, --header-file, and --nounput flags +# and supports processing our scanner.l. +# +if(WIN32) + set(NULL_DEVICE "NUL:") +else() + set(NULL_DEVICE "/dev/null") +endif() +execute_process(COMMAND ${LEX_EXECUTABLE} -P pcap_ --header-file=${NULL_DEVICE} --nounput -t ${pcap_SOURCE_DIR}/scanner.l + OUTPUT_QUIET RESULT_VARIABLE EXIT_STATUS) +if(NOT EXIT_STATUS EQUAL 0) + message(FATAL_ERROR "${LEX_EXECUTABLE} is insufficient to compile libpcap. +libpcap requires Flex 2.5.31 or later, or a compatible version of lex. +If a suitable version of Lex/Flex is available as a non-standard command +and/or not in the PATH, you can specify it using the LEX environment +variable. That said, on some systems the error can mean that Flex/Lex is +actually acceptable, but m4 is not. Likewise, if a suitable version of +m4 (such as GNU M4) is available but has not been detected, you can +specify it using the M4 environment variable.") +endif() + add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/scanner.c ${CMAKE_CURRENT_BINARY_DIR}/scanner.h SOURCE ${pcap_SOURCE_DIR}/scanner.l @@ -1816,22 +2574,56 @@ find_program(YACC_EXECUTABLE NAMES bison win_bison byacc yacc) if(YACC_EXECUTABLE STREQUAL "YACC_EXECUTABLE-NOTFOUND") message(FATAL_ERROR "Neither bison nor win_bison nor byacc nor yacc was found.") endif() + +if(YACC_EXECUTABLE MATCHES "byacc" OR YACC_EXECUTABLE MATCHES "yacc") + # + # Make sure this is Berkeley YACC, not AT&T YACC; + # the latter doesn't support reentrant parsers. + # Run it with "-V"; that succeeds and reports the + # version number with Berkeley YACC, but will + # (probably) fail with various vendor flavors + # of AT&T YACC. + # + # Hopefully this also eliminates any versions + # of Berkeley YACC that don't support reentrant + # parsers, if there are any. + # + execute_process(COMMAND ${YACC_EXECUTABLE} -V OUTPUT_QUIET + RESULT_VARIABLE EXIT_STATUS) + if(NOT EXIT_STATUS EQUAL 0) + message(FATAL_ERROR "${YACC_EXECUTABLE} is insufficient to compile libpcap. +libpcap requires Bison, a newer version of Berkeley YACC with support +for reentrant parsers, or another YACC compatible with them.") + endif() + # + # Berkeley YACC doesn't support "%define api.pure", so use + # "%pure-parser". + # + set(REENTRANT_PARSER "%pure-parser") +else() + # + # Bison prior to 2.4(.1) doesn't support "%define api.pure", so use + # "%pure-parser". + # + execute_process(COMMAND ${YACC_EXECUTABLE} -V OUTPUT_VARIABLE bison_full_version) + string(REGEX MATCH "[1-9][0-9]*[.][0-9]+" bison_major_minor ${bison_full_version}) + if (bison_major_minor VERSION_LESS "2.4") + set(REENTRANT_PARSER "%pure-parser") + else() + set(REENTRANT_PARSER "%define api.pure") + endif() +endif() + message(STATUS "Parser generator: ${YACC_EXECUTABLE}") # # Create custom command for the scanner. -# Find out whether it's Bison or not by looking at the last component -# of the path (without a .exe extension, if this is Windows). # -get_filename_component(YACC_NAME ${YACC_EXECUTABLE} NAME_WE) -if("${YACC_NAME}" STREQUAL "bison" OR "${YACC_NAME}" STREQUAL "win_bison") - set(YACC_COMPATIBILITY_FLAG "-y") -endif() add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/grammar.c ${CMAKE_CURRENT_BINARY_DIR}/grammar.h - SOURCE ${pcap_SOURCE_DIR}/grammar.y - COMMAND ${YACC_EXECUTABLE} ${YACC_COMPATIBILITY_FLAG} -p pcap_ -o ${CMAKE_CURRENT_BINARY_DIR}/grammar.c -d ${pcap_SOURCE_DIR}/grammar.y - DEPENDS ${pcap_SOURCE_DIR}/grammar.y + SOURCE ${pcap_BINARY_DIR}/grammar.y + COMMAND ${YACC_EXECUTABLE} -p pcap_ -o ${CMAKE_CURRENT_BINARY_DIR}/grammar.c -d ${pcap_BINARY_DIR}/grammar.y + DEPENDS ${pcap_BINARY_DIR}/grammar.y ) # @@ -1868,6 +2660,9 @@ if(CMAKE_SYSTEM_NAME STREQUAL "AIX") # we use them to load the BPF module. # set(PCAP_LINK_LIBRARIES ${PCAP_LINK_LIBRARIES} odm cfg) + set(LIBS "${LIBS} -lodm -lcfg") + set(LIBS_STATIC "${LIBS_STATIC} -lodm -lcfg") + set(LIBS_PRIVATE "${LIBS_PRIVATE} -lodm -lcfg") endif() elseif(CMAKE_SYSTEM_NAME STREQUAL "HP-UX") if(CMAKE_SYSTEM_VERSION MATCHES "[A-Z.]*9\.[0-9]*") @@ -1906,7 +2701,7 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "IRIX" OR CMAKE_SYSTEM_NAME STREQUAL "IRIX64") set(MAN_MISC_INFO 5) elseif(CMAKE_SYSTEM_NAME STREQUAL "OSF1") # - # DEC OSF/1, a/k/a Digial UNIX, a/k/a Tru64 UNIX. + # DEC OSF/1, a/k/a Digital UNIX, a/k/a Tru64 UNIX. # Use Tru64 UNIX conventions for man pages; they're the same as the # System V conventions except that they use section 8 for # administrative commands and daemons. @@ -1939,6 +2734,11 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "SunOS" AND CMAKE_SYSTEM_VERSION MATCHES "5[.] set(MAN_MISC_INFO 5) set(MAN_DEVICES 7D) endif() +elseif(CMAKE_SYSTEM_NAME STREQUAL "Haiku") + # + # Haiku needs _BSD_SOURCE for the _IO* macros because it doesn't use them. + # + add_definitions(-D_BSD_SOURCE) endif() source_group("Source Files" FILES ${PROJECT_SOURCE_LIST_C}) @@ -1969,7 +2769,7 @@ add_subdirectory(testprogs) # # See # -# http://public.kitware.com/pipermail/cmake/2013-August/055510.html +# https://public.kitware.com/pipermail/cmake/2013-August/055510.html # add_custom_target(SerializeTarget DEPENDS @@ -2000,6 +2800,10 @@ if(BUILD_SHARED_LIBS) # set_target_properties(${LIBRARY_NAME} PROPERTIES DEFINE_SYMBOL pcap_EXPORTS) + if(NOT "${LINKER_FLAGS}" STREQUAL "") + set_target_properties(${LIBRARY_NAME} PROPERTIES + LINK_FLAGS "${LINKER_FLAGS}") + endif() endif(BUILD_SHARED_LIBS) add_library(${LIBRARY_NAME}_static STATIC @@ -2142,21 +2946,25 @@ if(APPLE AND "${CMAKE_OSX_ARCHITECTURES}" STREQUAL "") # captures.) # set(OSX_LIBRARY_ARCHITECTURES "x86_64;i386;ppc") - else() + elseif(SYSTEM_VERSION_MAJOR GREATER 10 AND SYSTEM_VERSION_MAJOR LESS 19) # - # Post-Snow Leopard. Build for x86-64 and 32-bit x86, - # with x86-64 first. (That's what Apple does) - # XXX - update if and when Apple drops support - # for 32-bit x86 code and if and when Apple adds - # ARM-based Macs. (You're on your own for iOS etc.) + # Post-Snow Leopard, pre-Catalina. Build for x86-64 + # and 32-bit x86, with x86-64 first. (That's what Apple does) # - # XXX - check whether we *can* build for i386 and, if not, - # suggest that the user install the /usr/include headers if - # they want to build fat. + # First, check whether we're building with OpenSSL. + # If so, don't bother trying to build fat. # - cmake_push_check_state() - set(CMAKE_REQUIRED_FLAGS "-arch i386") - check_c_source_compiles( + if(HAVE_OPENSSL) + set(X86_32_BIT_SUPPORTED NO) + set(OSX_LIBRARY_ARCHITECTURES "x86_64") + message(WARNING "We're assuming the OpenSSL libraries are 64-bit only, so we're not compiling for 32-bit x86") + else() + # + # Now, check whether we *can* build for i386. + # + cmake_push_check_state() + set(CMAKE_REQUIRED_FLAGS "-arch i386") + check_c_source_compiles( "int main(void) { @@ -2164,20 +2972,80 @@ main(void) } " X86_32_BIT_SUPPORTED) - cmake_pop_check_state() - if(X86_32_BIT_SUPPORTED) - set(OSX_LIBRARY_ARCHITECTURES "x86_64;i386") + cmake_pop_check_state() + if(X86_32_BIT_SUPPORTED) + set(OSX_LIBRARY_ARCHITECTURES "x86_64;i386") + else() + set(OSX_LIBRARY_ARCHITECTURES "x86_64") + # + # We can't build fat; suggest that the user install the + # /usr/include headers if they want to build fat. + # + if(SYSTEM_VERSION_MAJOR LESS 18) + # + # Pre-Mojave; the command-line tools should be sufficient to + # enable 32-bit x86 builds. + # + message(WARNING "Compiling for 32-bit x86 gives an error; try installing the command-line tools") + else() + message(WARNING "Compiling for 32-bit x86 gives an error; try installing the command-line tools and, after that, installing the /usr/include headers from the /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg package") + endif() + endif() + endif() + elseif(SYSTEM_VERSION_MAJOR EQUAL 19) + # + # Catalina. Build libraries and executables + # only for x86-64. (That's what Apple does; + # 32-bit x86 binaries are not supported on + # Catalina.) + # + set(OSX_LIBRARY_ARCHITECTURES "x86_64") + else() + # + # Post-Catalina. Build libraries and + # executables for x86-64 and ARM64. + # (That's what Apple does, except they + # build for arm64e, which may include + # some of the pointer-checking extensions.) + # + # If we're building with libssl, make sure + # we can build fat with it (i.e., that it + # was built fat); if we can't, don't set + # the target architectures, and just + # build for the host we're on. + # + # Otherwise, just add both of them. + # + if(HAVE_OPENSSL) + cmake_push_check_state() + set(CMAKE_REQUIRED_FLAGS "-arch x86_64 -arch arm64") + set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR}) + set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES}) + # + # We must test whether this compiles and links, so + # check_symbol_exists() isn't sufficient. + # + # SSL_library_init() may be a macro that's #defined + # to be the real function to call, so we have to + # include , and check_function_exists() + # isn't sufficient. + # + check_c_source_compiles( +"#include +int +main(void) +{ + SSL_library_init(); + return 0; +} +" + FAT_SSL_BUILDS_SUPPORTED) + cmake_pop_check_state() + if(FAT_SSL_BUILDS_SUPPORTED) + set(OSX_LIBRARY_ARCHITECTURES "x86_64;arm64") + endif() else() - set(OSX_LIBRARY_ARCHITECTURES "x86_64") - if(SYSTEM_VERSION_MAJOR LESS 18) - # - # Pre-Mojave; the command-line tools should be sufficient to - # enable 32-bit x86 builds. - # - message(WARNING "Compiling for 32-bit x86 gives an error; try installing the command-line tools") - else() - message(WARNING "Compiling for 32-bit x86 gives an error; try installing the command-line tools and, after that, installing the /usr/include headers from the /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg package") - endif() + set(OSX_LIBRARY_ARCHITECTURES "x86_64;arm64") endif() endif() if(BUILD_SHARED_LIBS) @@ -2194,6 +3062,12 @@ endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmakeconfig.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) +###################################### +# Write out the grammar.y file +###################################### + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/grammar.y.in ${CMAKE_CURRENT_BINARY_DIR}/grammar.y @ONLY) + ###################################### # Install pcap library, include files, and man pages ###################################### @@ -2209,25 +3083,24 @@ set(LIBRARY_NAME_STATIC ${LIBRARY_NAME}_static) function(install_manpage_symlink SOURCE TARGET MANDIR) if(MINGW) - find_program(LINK_EXECUTABLE ln) - if(LINK_EXECUTABLE) - set(LINK_COMMAND "\"${LINK_EXECUTABLE}\" \"-s\" \"${SOURCE}\" \"${TARGET}\"") - else(LINK_EXECUTABLE) - message(FATAL_ERROR "ln (http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ln.html) not found.") - endif(LINK_EXECUTABLE) + # + # If we haven't found an ln executable with MinGW, we don't try + # generating and installing the man pages, so if we get here, + # we've found that executable. + set(LINK_COMMAND "\"${LINK_EXECUTABLE}\" \"-s\" \"${SOURCE}\" \"${TARGET}\"") else(MINGW) set(LINK_COMMAND "\"${CMAKE_COMMAND}\" \"-E\" \"create_symlink\" \"${SOURCE}\" \"${TARGET}\"") endif(MINGW) install(CODE - "message(STATUS \"Symlinking: ${CMAKE_INSTALL_PREFIX}/${MANDIR}/${SOURCE} to ${TARGET}\") + "message(STATUS \"Symlinking: \$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${MANDIR}/${SOURCE} to ${TARGET}\") execute_process( COMMAND \"${CMAKE_COMMAND}\" \"-E\" \"remove\" \"${TARGET}\" - WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}/${MANDIR} + WORKING_DIRECTORY \$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${MANDIR} ) execute_process( COMMAND ${LINK_COMMAND} - WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}/${MANDIR} + WORKING_DIRECTORY \$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${MANDIR} RESULT_VARIABLE EXIT_STATUS ) if(NOT EXIT_STATUS EQUAL 0) @@ -2271,6 +3144,7 @@ set(MAN3PCAP_NOEXPAND pcap_get_required_select_timeout.3pcap pcap_get_selectable_fd.3pcap pcap_geterr.3pcap + pcap_init.3pcap pcap_inject.3pcap pcap_is_swapped.3pcap pcap_lib_version.3pcap @@ -2298,33 +3172,42 @@ set(MAN3PCAP_NOEXPAND pcap_tstamp_type_name_to_val.3pcap pcap_tstamp_type_val_to_name.3pcap ) -set(MANFILE_EXPAND pcap-savefile.manfile.in) +set(MANFILE_EXPAND + pcap-savefile.manfile.in +) set(MANMISC_EXPAND pcap-filter.manmisc.in pcap-linktype.manmisc.in pcap-tstamp.manmisc.in ) -if(NOT BUILD_SHARED_LIBS) - unset(LIBRARY_NAME) -endif(NOT BUILD_SHARED_LIBS) +if(BUILD_SHARED_LIBS) + set(LIBRARIES_TO_INSTALL "${LIBRARY_NAME}" "${LIBRARY_NAME_STATIC}") +else(BUILD_SHARED_LIBS) + set(LIBRARIES_TO_INSTALL "${LIBRARY_NAME_STATIC}") +endif(BUILD_SHARED_LIBS) -if(WIN32) +if(WIN32 OR CYGWIN OR MSYS) + # + # XXX - according to the CMake documentation, WIN32 is set if + # the target is Windows; would there ever be a case where + # CYGWIN or MSYS are set but WIN32 *isn't* set? + # if(MSVC AND CMAKE_SIZEOF_VOID_P EQUAL 8) # - # Install 64-bit code built with MSVC in the amd64 subdirectories, + # Install 64-bit code built with MSVC in the x64 subdirectories, # as that's where it expects it to be. # - install(TARGETS ${LIBRARY_NAME} ${LIBRARY_NAME_STATIC} - RUNTIME DESTINATION bin/amd64 - LIBRARY DESTINATION lib/amd64 - ARCHIVE DESTINATION lib/amd64) + install(TARGETS ${LIBRARIES_TO_INSTALL} + RUNTIME DESTINATION bin/x64 + LIBRARY DESTINATION lib/x64 + ARCHIVE DESTINATION lib/x64) if(NOT MINGW) install(FILES $/${LIBRARY_NAME_STATIC}.pdb - DESTINATION bin/amd64 OPTIONAL) + DESTINATION bin/x64 OPTIONAL) if(BUILD_SHARED_LIBS) install(FILES $ - DESTINATION bin/amd64 OPTIONAL) + DESTINATION bin/x64 OPTIONAL) endif(BUILD_SHARED_LIBS) endif(NOT MINGW) else(MSVC AND CMAKE_SIZEOF_VOID_P EQUAL 8) @@ -2333,22 +3216,22 @@ if(WIN32) # in the top-level directories, as those are where they # expect it to be. # - install(TARGETS ${LIBRARY_NAME} ${LIBRARY_NAME_STATIC} + install(TARGETS ${LIBRARIES_TO_INSTALL} RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib) - if(NOT MINGW) + if(MSVC) install(FILES $/${LIBRARY_NAME_STATIC}.pdb DESTINATION bin OPTIONAL) if(BUILD_SHARED_LIBS) install(FILES $ DESTINATION bin OPTIONAL) endif(BUILD_SHARED_LIBS) - endif(NOT MINGW) + endif(MSVC) endif(MSVC AND CMAKE_SIZEOF_VOID_P EQUAL 8) -else(WIN32) - install(TARGETS ${LIBRARY_NAME} ${LIBRARY_NAME_STATIC} DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}) -endif(WIN32) +else(WIN32 OR CYGWIN OR MSYS) + install(TARGETS ${LIBRARIES_TO_INSTALL} DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}) +endif(WIN32 OR CYGWIN OR MSYS) install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/pcap/ DESTINATION include/pcap) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/pcap.h DESTINATION include) @@ -2358,41 +3241,73 @@ install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/pcap-namedb.h DESTINATION include) # On UN*X, and on Windows when not using MSVC, generate libpcap.pc and # pcap-config and process man pages and arrange that they be installed. if(NOT MSVC) - set(PACKAGE_NAME ${LIBRARY_NAME}) set(prefix ${CMAKE_INSTALL_PREFIX}) set(exec_prefix "\${prefix}") set(includedir "\${prefix}/include") - set(libdir "\${exec_prefix}/lib") - if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR - CMAKE_SYSTEM_NAME STREQUAL "NetBSD" OR - CMAKE_SYSTEM_NAME STREQUAL "OpenBSD" OR - CMAKE_SYSTEM_NAME STREQUAL "DragonFly BSD" OR - CMAKE_SYSTEM_NAME STREQUAL "Linux" OR - CMAKE_SYSTEM_NAME STREQUAL "OSF1") - # - # Platforms where the linker is the GNU linker - # or accepts command-line arguments like - # those the GNU linker accepts. - # - set(V_RPATH_OPT "-Wl,-rpath,") - elseif(CMAKE_SYSTEM_NAME STREQUAL "SunOS" AND CMAKE_SYSTEM_VERSION MATCHES "5[.][0-9.]*") - # - # SunOS 5.x. - # - # XXX - this assumes GCC is using the Sun linker, - # rather than the GNU linker. - # - set(V_RPATH_OPT "-Wl,-R,") - else() - # - # No option needed to set the RPATH. - # - set(V_RPATH_OPT "") + set(libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}") + + # + # If this is a platform where we need to have the .pc file and + # pcap-config script supply an rpath option to specify the directory + # in which the libpcap shared library is installed, and the install + # prefix /usr (meaning we're not installing a system library), + # provide the rpath option. + # + # (We must check CMAKE_INSTALL_PREFIX, as the library directory + # isn't necessarily /usr/lib in this case - for example, Linux + # distributions for 64-bit platforms that also provide support for + # binaries for a 32-bit version of the platform may put the 64-bit + # libraries, the 32-bit libraries, or both in directories other than + # /usr/lib.) + # + # In AIX, do we have to do this? + # + # In Darwin-based OSes, the full paths of the shared libraries with + # which the program was linked are stored in the executable, so we + # don't need to provide an rpath option. + # + # With the HP-UX linker, directories specified with -L are, by + # default, added to the run-time search path, so we don't need to + # supply them. + # + # For Tru64 UNIX, "-rpath" works with DEC's^WCompaq's^WHP's C + # compiler for Alpha, but isn't documented as working with GCC, and + # no GCC-compatible option is documented as working with the DEC + # compiler. If anybody needs this on Tru64/Alpha, they're welcome + # to figure out a way to make it work. + # + # This must *not* depend on the compiler, as, on platforms where + # there's a GCC-compatible compiler and a vendor compiler, we need + # to work with both. + # + if(NOT CMAKE_INSTALL_PREFIX STREQUAL "/usr") + if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "NetBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "OpenBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "DragonFly BSD" OR + CMAKE_SYSTEM_NAME STREQUAL "Linux") + # + # Platforms where the "native" C compiler is GCC or accepts + # compatible command-line arguments, and the "native" linker + # is the GNU linker or accepts compatible command-line + # arguments. + # + set(RPATH "-Wl,-rpath,\${libdir}") + elseif(CMAKE_SYSTEM_NAME STREQUAL "SunOS" AND CMAKE_SYSTEM_VERSION MATCHES "5[.][0-9.]*") + # + # SunOS 5.x. + # + # Sun/Oracle's linker, the GNU linker, and GNU-compatible + # linkers all support -R. + # + set(RPATH "-Wl,-R,\${libdir}") + else() + # + # No option needed to set the RPATH. + # + set(RPATH "") + endif() endif() - set(LIBS "") - foreach(LIB ${PCAP_LINK_LIBRARIES}) - set(LIBS "${LIBS} -l${LIB}") - endforeach(LIB) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pcap-config.in ${CMAKE_CURRENT_BINARY_DIR}/pcap-config @ONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libpcap.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libpcap.pc @ONLY) install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/pcap-config DESTINATION bin) @@ -2404,54 +3319,64 @@ if(NOT MSVC) # For each section of the manual for which we have man pages # that require macro expansion, do the expansion. # - set(MAN1 "") - foreach(MANPAGE ${MAN1_NOEXPAND}) - set(MAN1 ${MAN1} ${CMAKE_CURRENT_SOURCE_DIR}/${MANPAGE}) - endforeach(MANPAGE) - install(FILES ${MAN1} DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) + # If this is MinGW, maybe we have a UN*X-style ln command and + # maybe we don't. (No, we do *NOT* require MSYS!) If we don't + # have it, don't do the man pages. + # + if(MINGW) + find_program(LINK_EXECUTABLE ln) + endif(MINGW) + if(UNIX OR (MINGW AND LINK_EXECUTABLE)) + set(MAN1 "") + foreach(MANPAGE ${MAN1_NOEXPAND}) + set(MAN1 ${MAN1} ${CMAKE_CURRENT_SOURCE_DIR}/${MANPAGE}) + endforeach(MANPAGE) + install(FILES ${MAN1} DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) - set(MAN3PCAP "") - foreach(MANPAGE ${MAN3PCAP_NOEXPAND}) - set(MAN3PCAP ${MAN3PCAP} ${CMAKE_CURRENT_SOURCE_DIR}/${MANPAGE}) - endforeach(MANPAGE) - foreach(TEMPLATE_MANPAGE ${MAN3PCAP_EXPAND}) - string(REPLACE ".in" "" MANPAGE ${TEMPLATE_MANPAGE}) - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${TEMPLATE_MANPAGE} ${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE} @ONLY) - set(MAN3PCAP ${MAN3PCAP} ${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE}) - endforeach(TEMPLATE_MANPAGE) - install(FILES ${MAN3PCAP} DESTINATION ${CMAKE_INSTALL_MANDIR}/man3) - install_manpage_symlink(pcap_datalink_val_to_name.3pcap pcap_datalink_val_to_description.3pcap ${CMAKE_INSTALL_MANDIR}/man3) - install_manpage_symlink(pcap_dump_open.3pcap pcap_dump_fopen.3pcap ${CMAKE_INSTALL_MANDIR}/man3) - install_manpage_symlink(pcap_findalldevs.3pcap pcap_freealldevs.3pcap ${CMAKE_INSTALL_MANDIR}/man3) - install_manpage_symlink(pcap_geterr.3pcap pcap_perror.3pcap ${CMAKE_INSTALL_MANDIR}/man3) - install_manpage_symlink(pcap_inject.3pcap pcap_sendpacket.3pcap ${CMAKE_INSTALL_MANDIR}/man3) - install_manpage_symlink(pcap_list_datalinks.3pcap pcap_free_datalinks.3pcap ${CMAKE_INSTALL_MANDIR}/man3) - install_manpage_symlink(pcap_list_tstamp_types.3pcap pcap_free_tstamp_types.3pcap ${CMAKE_INSTALL_MANDIR}/man3) - install_manpage_symlink(pcap_loop.3pcap pcap_dispatch.3pcap ${CMAKE_INSTALL_MANDIR}/man3) - install_manpage_symlink(pcap_major_version.3pcap pcap_minor_version.3pcap ${CMAKE_INSTALL_MANDIR}/man3) - install_manpage_symlink(pcap_next_ex.3pcap pcap_next.3pcap ${CMAKE_INSTALL_MANDIR}/man3) - install_manpage_symlink(pcap_open_dead.3pcap pcap_open_dead_with_tstamp_precision.3pcap ${CMAKE_INSTALL_MANDIR}/man3) - install_manpage_symlink(pcap_open_offline.3pcap pcap_open_offline_with_tstamp_precision.3pcap ${CMAKE_INSTALL_MANDIR}/man3) - install_manpage_symlink(pcap_open_offline.3pcap pcap_fopen_offline.3pcap ${CMAKE_INSTALL_MANDIR}/man3) - install_manpage_symlink(pcap_open_offline.3pcap pcap_fopen_offline_with_tstamp_precision.3pcap ${CMAKE_INSTALL_MANDIR}/man3) - install_manpage_symlink(pcap_tstamp_type_val_to_name.3pcap pcap_tstamp_type_val_to_description.3pcap ${CMAKE_INSTALL_MANDIR}/man3) - install_manpage_symlink(pcap_setnonblock.3pcap pcap_getnonblock.3pcap ${CMAKE_INSTALL_MANDIR}/man3) + set(MAN3PCAP "") + foreach(MANPAGE ${MAN3PCAP_NOEXPAND}) + set(MAN3PCAP ${MAN3PCAP} ${CMAKE_CURRENT_SOURCE_DIR}/${MANPAGE}) + endforeach(MANPAGE) + foreach(TEMPLATE_MANPAGE ${MAN3PCAP_EXPAND}) + string(REPLACE ".in" "" MANPAGE ${TEMPLATE_MANPAGE}) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${TEMPLATE_MANPAGE} ${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE} @ONLY) + set(MAN3PCAP ${MAN3PCAP} ${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE}) + endforeach(TEMPLATE_MANPAGE) + install(FILES ${MAN3PCAP} DESTINATION ${CMAKE_INSTALL_MANDIR}/man3) + install_manpage_symlink(pcap_datalink_val_to_name.3pcap pcap_datalink_val_to_description.3pcap ${CMAKE_INSTALL_MANDIR}/man3) + install_manpage_symlink(pcap_datalink_val_to_name.3pcap pcap_datalink_val_to_description_or_dlt.3pcap ${CMAKE_INSTALL_MANDIR}/man3) + install_manpage_symlink(pcap_dump_open.3pcap pcap_dump_fopen.3pcap ${CMAKE_INSTALL_MANDIR}/man3) + install_manpage_symlink(pcap_findalldevs.3pcap pcap_freealldevs.3pcap ${CMAKE_INSTALL_MANDIR}/man3) + install_manpage_symlink(pcap_geterr.3pcap pcap_perror.3pcap ${CMAKE_INSTALL_MANDIR}/man3) + install_manpage_symlink(pcap_inject.3pcap pcap_sendpacket.3pcap ${CMAKE_INSTALL_MANDIR}/man3) + install_manpage_symlink(pcap_list_datalinks.3pcap pcap_free_datalinks.3pcap ${CMAKE_INSTALL_MANDIR}/man3) + install_manpage_symlink(pcap_list_tstamp_types.3pcap pcap_free_tstamp_types.3pcap ${CMAKE_INSTALL_MANDIR}/man3) + install_manpage_symlink(pcap_loop.3pcap pcap_dispatch.3pcap ${CMAKE_INSTALL_MANDIR}/man3) + install_manpage_symlink(pcap_major_version.3pcap pcap_minor_version.3pcap ${CMAKE_INSTALL_MANDIR}/man3) + install_manpage_symlink(pcap_next_ex.3pcap pcap_next.3pcap ${CMAKE_INSTALL_MANDIR}/man3) + install_manpage_symlink(pcap_open_dead.3pcap pcap_open_dead_with_tstamp_precision.3pcap ${CMAKE_INSTALL_MANDIR}/man3) + install_manpage_symlink(pcap_open_offline.3pcap pcap_open_offline_with_tstamp_precision.3pcap ${CMAKE_INSTALL_MANDIR}/man3) + install_manpage_symlink(pcap_open_offline.3pcap pcap_fopen_offline.3pcap ${CMAKE_INSTALL_MANDIR}/man3) + install_manpage_symlink(pcap_open_offline.3pcap pcap_fopen_offline_with_tstamp_precision.3pcap ${CMAKE_INSTALL_MANDIR}/man3) + install_manpage_symlink(pcap_tstamp_type_val_to_name.3pcap pcap_tstamp_type_val_to_description.3pcap ${CMAKE_INSTALL_MANDIR}/man3) + install_manpage_symlink(pcap_setnonblock.3pcap pcap_getnonblock.3pcap ${CMAKE_INSTALL_MANDIR}/man3) - set(MANFILE "") - foreach(TEMPLATE_MANPAGE ${MANFILE_EXPAND}) - string(REPLACE ".manfile.in" ".${MAN_FILE_FORMATS}" MANPAGE ${TEMPLATE_MANPAGE}) - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${TEMPLATE_MANPAGE} ${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE} @ONLY) - set(MANFILE ${MANFILE} ${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE}) - endforeach(TEMPLATE_MANPAGE) - install(FILES ${MANFILE} DESTINATION ${CMAKE_INSTALL_MANDIR}/man${MAN_FILE_FORMATS}) + set(MANFILE "") + foreach(TEMPLATE_MANPAGE ${MANFILE_EXPAND}) + string(REPLACE ".manfile.in" ".${MAN_FILE_FORMATS}" MANPAGE ${TEMPLATE_MANPAGE}) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${TEMPLATE_MANPAGE} ${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE} @ONLY) + set(MANFILE ${MANFILE} ${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE}) + endforeach(TEMPLATE_MANPAGE) + install(FILES ${MANFILE} DESTINATION ${CMAKE_INSTALL_MANDIR}/man${MAN_FILE_FORMATS}) - set(MANMISC "") - foreach(TEMPLATE_MANPAGE ${MANMISC_EXPAND}) - string(REPLACE ".manmisc.in" ".${MAN_MISC_INFO}" MANPAGE ${TEMPLATE_MANPAGE}) - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${TEMPLATE_MANPAGE} ${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE} @ONLY) - set(MANMISC ${MANMISC} ${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE}) - endforeach(TEMPLATE_MANPAGE) - install(FILES ${MANMISC} DESTINATION ${CMAKE_INSTALL_MANDIR}/man${MAN_MISC_INFO}) + set(MANMISC "") + foreach(TEMPLATE_MANPAGE ${MANMISC_EXPAND}) + string(REPLACE ".manmisc.in" ".${MAN_MISC_INFO}" MANPAGE ${TEMPLATE_MANPAGE}) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${TEMPLATE_MANPAGE} ${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE} @ONLY) + set(MANMISC ${MANMISC} ${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE}) + endforeach(TEMPLATE_MANPAGE) + install(FILES ${MANMISC} DESTINATION ${CMAKE_INSTALL_MANDIR}/man${MAN_MISC_INFO}) + endif(UNIX OR (MINGW AND LINK_EXECUTABLE)) endif(NOT MSVC) # uninstall target diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 69b597260f79..fb22c5e38159 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,10 +14,10 @@ above), please navigate to https://github.com/the-tcpdump-group/libpcap/issues and check if the problem has already been reported. If it has not, please open a new issue and provide the following details: -* libpcap version (e.g. from tcpdump --version) +* libpcap version (e.g. from `tcpdump --version`) * operating system name and version and any other details that may be relevant - (uname -a, compiler name and version, CPU type etc.) -* configure flags if any were used + (`uname -a`, compiler name and version, CPU type etc.) +* `configure` or `cmake` flags if any were used * statement of the problem * steps to reproduce diff --git a/CREDITS b/CREDITS index f7abc1f3222e..4b820ee7b9ac 100644 --- a/CREDITS +++ b/CREDITS @@ -3,29 +3,43 @@ This file lists people who have contributed to libpcap. The current maintainers (in alphabetical order): Denis Ovsienko Francois-Xavier Le Bail - Guy Harris + Guy Harris Michael Richardson Additional people who have contributed patches (in alphabetical order): + Adrian Budau Akos Vandra Alan Bawden Albert Chin + Alexander Galanin Alexander 'Leo' Bergolth Alexey Kuznetsov + Alex Smith <44322503+MadAlexUK at users dot noreply dot github dot com> + Alfredo Alvarez Fernandez Ali Abdulkadir Alon Bar-Lev + Anders Broman Andres Perera Andrew Brown Ani Sinha + Anthony Kirby Antti Kantee Arien Vijn Arkadiusz Miskiewicz Armando L. Caro Jr. Assar Westerlund + Atzm Watanabe + Baptiste Peugnez + Baruch Siach Bill Parker + Biswapriyo Nath + blazeable + bleader Brent Cook Brian Ginsbach + B. Scott Michel + Cedric Cellier Charles M. Hannum Chris G. Demetriou Chris Lightfoot @@ -34,24 +48,38 @@ Additional people who have contributed patches (in alphabetical order): Christian Bell Christian Peron Christian Svensson + Christopher K Lee + Daniel Borkmann Daniele Orlandi + Daniel Lublin + Daniel Miller + Dario Lombardo Darren Lim Darren Reed + Dave Barach David Clark David Kaelbling + David Karoly David Ward David Young Dean Gaudet dhruv + Dmytro Ovdiienko Don Ebright Dug Song Dustin Spicuzza dzejarczech Edward Sheldrake + Eli Schwartz Eric Anderson Erik de Castro Lopo + Fedor Sakharov + Felix Janda Felix Obenhuber + fghzxm Florent Drouin + Florian Fainelli + François Revol Franz Schaefer frederich Fulko Hew @@ -59,6 +87,7 @@ Additional people who have contributed patches (in alphabetical order): Gabor Tatarka Garrett Cooper George Neville-Neil + Gerald Combs Gerard Garcia Gianluca Varenni Gilbert Hoyek @@ -71,83 +100,115 @@ Additional people who have contributed patches (in alphabetical order): Gustavo Zacarias Hagen Paul Pfeifer Henri Doreau + Hiroaki KAWAI Hyung Sik Yoon Igor Khristophorov + Jakub Sitnicki Jakub Zawadzki + James Ko Jan-Philip Velders Jason R. Thorpe Javier Achirica Jean-Louis Charton Jean Tourrilhes Jefferson Ogata + Jerome Duval Jesper Dangaard Brouer Jesper Peterson Jesse Gross + JHA + jingyu yang Jiri Slaby + João Valverde Joerg Mayer John Bankier Jon Lindgren Jon Smirl Jorge Boncompte [DTI2] + jromanr Juergen Schoenwaelder Julien Moutinho Jung-uk Kim Kazushi Sugyo + Kevin Boulain Klaus Klein Koryn Grant Kris Katterjohn Krzysztof Halasa Lennert Buytenhek + Li kunyu + lixiaoyan Lorenzo Cavallaro Loris Degioanni Love Hörnquist-Åstrand Luis MartinGarcia + lxy <391861737 at qq dot com> Maciej W. Rozycki Mansour Behabadi Marcus Felipe Pereira + Mario J. Rugiero Mark C. Brown Mark Johnston + Mark Marshall Mark Pizzolato Markus Mayer Martin Husemann Márton Németh + Matt Eaton Matthew Luckie + Matthias Hannig + Matwey V. Kornilov + maxice8 Max Laier Michal Kubecek Michal Labedzki + Michal Ruprich Michal Sekletar Mike Frysinger Mike Kershaw Mike Wiacek + Milosz Kaniewski Miroslav Lichvar Monroe Williams + Myricom Help + Nan Xiao + nic-kaczinsky <68271784+nic-kaczinsky at users dot noreply dot github dot com> + Nick Kelsey Nicolas Dade Niko Delarich N. Leiten + nnposter Octavian Cerna Olaf Kirch Ollie Wild + Ondřej Hošek Onno van der Linden + Orgad Shaneh + Ørjan Malde Paolo Abeni Patrick Marie - Patrick McHardy + Patrick McHardy Paul Mundt Pavel Kankovsky + Pawel Brzezinski Pawel Pokrywka Peter Fales Peter Jeremy Peter Volkov + Petr Vorel + Philippe Antoine Phil Wood Rafal Maszkowski + ramin Richard Stearn Rick Jones Robert Edmonds Roberto Mariani - Rongxi Li Roland Dreier Romain Francoise + Rongxi Li Sagun Shakya Scott Barron Scott Gifford @@ -155,23 +216,39 @@ Additional people who have contributed patches (in alphabetical order): Sebastian Krahmer Sebastien Roy Sepherosa Ziehau + Shane Kerr Shaun Clowes + solofox Solomon Peachy Stefan Hudson Stephen Donnelly + Steve Karg + stubbfel Takashi Yamamoto Tanaka Shin-ya + Thomas Habets + Thomas Petazzoni Tobias Poschwatta + Tomasz Moń + Tommy Beadle Tony Li Torsten Landschoff + Tymoteusz Blazejczyk Uns Lider Uwe Girlich + Vitaly Lavrov + Vivien Didelot + Vladimir Gladkov + Vladimir Marek + Walter Schell Wesley Shields Xianjie Zhang Xin Li Xue Jiang Qing + Yang Luo Yen Yen Lim Yoann Vandoorselaere + Yogesh Prasad Yvan Vanhullebus The original LBL crew: diff --git a/INSTALL.md b/INSTALL.md index 3a303fe0a6ce..d0a19d811a8b 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,105 +1,120 @@ -To build libpcap, run "./configure" (a shell script). The configure -script will determine your system attributes and generate an -appropriate Makefile from Makefile.in. Next run "make". If everything -goes well you can su to root and run "make install". However, you need -not install libpcap if you just want to build tcpdump; just make sure -the tcpdump and libpcap directory trees have the same parent -directory. +# libpcap installation notes +Libpcap can be built either with the configure script and `make`, or +with CMake and any build system supported by CMake. + +To build libpcap with the configure script and `make`: + +* Run `./configure` (a shell script). The configure script will +determine your system attributes and generate an appropriate `Makefile` +from `Makefile.in`. The configure script has a number of options to +control the configuration of libpcap; `./configure --help`` will show +them. + +* Next, run `make`. If everything goes well, you can +`su` to root and run `make install`. However, you need not install +libpcap if you just want to build tcpdump; just make sure the tcpdump +and libpcap directory trees have the same parent directory. + +To build libpcap with CMake and the build system of your choice, from +the command line: + +* Create a build directory into which CMake will put the build files it +generates; CMake does not work as well with builds done in the source +code directory as does the configure script. The build directory may be +created as a subdirectory of the source directory or as a directory +outside the source directory. + +* Change to the build directory and run CMake with the path from the +build directory to the source directory as an argument. The `-G` flag +can be used to select the CMake "generator" appropriate for the build +system you're using; various `-D` flags can be used to control the +configuration of libpcap. + +* Run the build tool. If everything goes well, you can `su` to root and +run the build tool with the `install` target. Building tcpdump from a +libpcap in a build directory is not supported. + +An `uninstall` target is supported with both `./configure` and CMake. + +***DO NOT*** run the build as root; there is no need to do so, running +anything as root that doesn't need to be run as root increases the risk +of damaging your system, and running the build as root will put files in +the build directory that are owned by root and that probably cannot be +overwritten, removed, or replaced except by root, which could cause +permission errors in subsequent builds. If configure says: configure: warning: cannot determine packet capture interface - configure: warning: (see INSTALL for more info) + configure: warning: (see INSTALL.md file for more info) + +or CMake says: + + cannot determine packet capture interface + + (see the INSTALL.md file for more info) then your system either does not support packet capture or your system does support packet capture but libpcap does not support that particular type. (If you have HP-UX, see below.) If your system uses a packet capture not supported by libpcap, please send us patches; don't forget to include an autoconf fragment suitable for use in -configure.ac. +`configure.ac`. -It is possible to override the default packet capture type, although -the circumstance where this works are limited. For example if you have -installed bpf under SunOS 4 and wish to build a snit libpcap: +It is possible to override the default packet capture type with the +`--with-pcap`` option to `./configure` or the `-DPCAP_TYPE` option to +CMake, although the circumstances where this works are limited. One +possible reason to do that would be to force a supported packet capture +type in the case where the configure or CMake scripts fails to detect +it. - ./configure --with-pcap=snit - -Another example is to force a supported packet capture type in the case -where the configure scripts fails to detect it. - -You will need an ANSI C compiler to build libpcap. The configure script -will abort if your compiler is not ANSI compliant. If this happens, use -the generally available GNU C compiler (GCC). +You will need a C99 compiler to build libpcap. The configure script +will abort if your compiler is not C99 compliant. If this happens, use +the generally available GNU C compiler (GCC) or Clang. You will need either Flex 2.5.31 or later, or a version of Lex compatible with it (if any exist), to build libpcap. The configure -script will abort if there isn't any such program. If you have an older -version of Flex, or don't have a compatible version of Lex, the current -version of flex is available at flex.sourceforge.net. +script will abort if there isn't any such program; CMake fails if Flex +or Lex cannot be found, but doesn't ensure that it's compatible with +Flex 2.5.31 or later. If you have an older version of Flex, or don't +have a compatible version of Lex, the current version of Flex is +available [here](https://github.com/westes/flex). You will need either Bison, Berkeley YACC, or a version of YACC compatible with them (if any exist), to build libpcap. The configure -script will abort if there isn't any such program. If you don't have -any such program, the current version of Bison can be found at -http://ftp.gnu.org/gnu/bison/ and the current version of Berkeley YACC -can be found at http://invisible-island.net/byacc/. +script will abort if there isn't any such program; CMake fails if Bison +or some form of YACC cannot be found, but doesn't ensure that it's +compatible with Bison or Berkeley YACC. If you don't have any such +program, the current version of Bison can be found +[here](https://ftp.gnu.org/gnu/bison/) and the current version of +Berkeley YACC can be found [here](https://invisible-island.net/byacc/). Sometimes the stock C compiler does not interact well with Flex and -Bison. The list of problems includes undefined references for alloca. +Bison. The list of problems includes undefined references for alloca(3). You can get around this by installing GCC. -If you use Solaris, there is a bug with bufmod(7) that is fixed in -Solaris 2.3.2 (aka SunOS 5.3.2). Setting a snapshot length with the -broken bufmod(7) results in data be truncated from the FRONT of the -packet instead of the end. The work around is to not set a snapshot -length but this results in performance problems since the entire packet -is copied to user space. If you must run an older version of Solaris, -there is a patch available from Sun; ask for bugid 1149065. After -installing the patch, use "setenv BUFMOD_FIXED" to enable use of -bufmod(7). However, we recommend you run a more current release of -Solaris. +## Linux specifics +On Linux, libpcap will not work if the kernel does not have the packet +socket option enabled; see [this file](doc/README.linux) for more +information. +## Solaris specifics If you use the SPARCompiler, you must be careful to not use the -/usr/ucb/cc interface. If you do, you will get bogus warnings and -perhaps errors. Either make sure your path has /opt/SUNWspro/bin -before /usr/ucb or else: +`/usr/ucb/cc` interface. If you do, you will get bogus warnings and +perhaps errors. Either make sure your path has `/opt/SUNWspro/bin` +before `/usr/ucb` or else: setenv CC /opt/SUNWspro/bin/cc -before running configure. (You might have to do a "make distclean" -if you already ran configure once). +before running configure. (You might have to do a `make distclean` +if you already ran `configure` once). -If you are trying to do packet capture with a FORE ATM card, you may or -may not be able to. They usually only release their driver in object -code so unless their driver supports packet capture, there's not much -libpcap can do. - -If you get an error like: - - tcpdump: recv_ack: bind error 0x??? - -when using DLPI, look for the DL_ERROR_ACK error return values, usually -in /usr/include/sys/dlpi.h, and find the corresponding value. - -Under {DEC OSF/1, Digital UNIX, Tru64 UNIX}, packet capture must be -enabled before it can be used. For instructions on how to enable packet -filter support, see: - - ftp://ftp.digital.com/pub/Digital/dec-faq/Digital-UNIX - -Look for the "How do I configure the Berkeley Packet Filter and capture -tcpdump traces?" item. - -Once you enable packet filter support, your OSF system will support bpf -natively. - -Under Ultrix, packet capture must be enabled before it can be used. For -instructions on how to enable packet filter support, see: - - ftp://ftp.digital.com/pub/Digital/dec-faq/ultrix +See [this file](doc/README.solaris.md) for more up to date +Solaris-related information. +## HP-UX specifics If you use HP-UX, you must have at least version 9 and either the -version of cc that supports ANSI C (cc -Aa) or else use the GNU C +version of `cc` that supports C99 (`cc -AC99`) or else use the GNU C compiler. You must also buy the optional streams package. If you don't have: @@ -113,10 +128,10 @@ need to install the "9.X LAN and DLPI drivers cumulative" patch The DLPI streams package is standard starting with HP-UX 10. The HP implementation of DLPI is a little bit eccentric. Unlike -Solaris, you must attach /dev/dlpi instead of the specific /dev/* +Solaris, you must attach `/dev/dlpi` instead of the specific `/dev/*` network pseudo device entry in order to capture packets. The PPA is based on the ifnet "index" number. Under HP-UX 9, it is necessary to -read /dev/kmem and the kernel symbol file (/hp-ux). Under HP-UX 10, +read `/dev/kmem` and the kernel symbol file (`/hp-ux`). Under HP-UX 10, DLPI can provide information for determining the PPA. It does not seem to be possible to trace the loopback interface. Unlike other DLPI implementations, PHYS implies MULTI and SAP and you get an error if you @@ -137,216 +152,126 @@ doing echo 'lanc_outbound_promisc_flag/W 1' | adb -w /stand/vmunix /dev/mem -You would have to arrange that this happen on reboots; the right way to +You would have to arrange that this happens on reboots; the right way to do that would probably be to put it into an executable script file -"/sbin/init.d/outbound_promisc" and making -"/sbin/rc2.d/S350outbound_promisc" a symbolic link to that script. +`/sbin/init.d/outbound_promisc` and making +`/sbin/rc2.d/S350outbound_promisc` a symbolic link to that script. Finally, testing shows that there can't be more than one simultaneous DLPI user per network interface. -If you use Linux, this version of libpcap is known to compile and run -under Red Hat 4.0 with the 2.0.25 kernel. It may work with earlier 2.X -versions but is guaranteed not to work with 1.X kernels. Running more -than one libpcap program at a time, on a system with a 2.0.X kernel, can -cause problems since promiscuous mode is implemented by twiddling the -interface flags from the libpcap application; the packet capture -mechanism in the 2.2 and later kernels doesn't have this problem. Also, -packet timestamps aren't very good. This appears to be due to haphazard -handling of the timestamp in the kernel. +See [this file](doc/README.hpux) for more information specific to HP-UX. -Note well: there is rumoured to be a version of tcpdump floating around -called 3.0.3 that includes libpcap and is supposed to support Linux. -You should be advised that neither the Network Research Group at LBNL -nor the Tcpdump Group ever generated a release with this version number. -The LBNL Network Research Group notes with interest that a standard -cracker trick to get people to install trojans is to distribute bogus -packages that have a version number higher than the current release. -They also noted with annoyance that 90% of the Linux related bug reports -they got are due to changes made to unofficial versions of their page. -If you are having trouble but aren't using a version that came from -tcpdump.org, please try that before submitting a bug report! - -On Linux, libpcap will not work if the kernel does not have the packet -socket option enabled; see the README.linux file for information about -this. - -If you use AIX, you may not be able to build libpcap from this release. -We do not have an AIX system in house so it's impossible for us to test -AIX patches submitted to us. We are told that you must link against -/lib/pse.exp, that you must use AIX cc or a GNU C compiler newer than -2.7.2, and that you may need to run strload before running a libpcap -application. - -Read the README.aix file for information on installing libpcap and +## AIX specifics +See [this file](doc/README.aix) for information on installing libpcap and configuring your system to be able to support libpcap. -If you use NeXTSTEP, you will not be able to build libpcap from this -release. +## other specifics +If you are trying to do packet capture with a FORE ATM card, you may or +may not be able to. They usually only release their driver in object +code so unless their driver supports packet capture, there's not much +libpcap can do. -If you use SINIX, you should be able to build libpcap from this -release. It is known to compile and run on SINIX-Y/N 5.42 with the C-DS -V1.0 or V1.1 compiler. But note that in some releases of SINIX, yacc -emits incorrect code; if grammar.y fails to compile, change every -occurence of: +If you get an error like: - #ifdef YYDEBUG + tcpdump: recv_ack: bind error 0x??? -to: - #if YYDEBUG +when using DLPI, look for the DL_ERROR_ACK error return values, usually +in `/usr/include/sys/dlpi.h`, and find the corresponding value. -Another workaround is to use flex and bison. - -If you use SCO, you might have trouble building libpcap from this -release. We do not have a machine running SCO and have not had reports -of anyone successfully building on it; the current release of libpcap -does not compile on SCO OpenServer 5. Although SCO apparently supports -DLPI to some extent, the DLPI in OpenServer 5 is very non-standard, and -it appears that completely new code would need to be written to capture -network traffic. SCO do not appear to provide tcpdump binaries for -OpenServer 5 or OpenServer 6 as part of SCO Skunkware: - - http://www.sco.com/skunkware/ - -If you use UnixWare, you might be able to build libpcap from this -release, or you might not. We do not have a machine running UnixWare, -so we have not tested it; however, SCO provide packages for libpcap -0.6.2 and tcpdump 3.7.1 in the UnixWare 7/Open UNIX 8 part of SCO -Skunkware, and the source package for libpcap 0.6.2 is not changed from -the libpcap 0.6.2 source release, so this release of libpcap might also -build without changes on UnixWare 7. - -If linking tcpdump fails with "Undefined: _alloca" when using bison on -a Sun4, your version of Bison is broken. In any case version 1.16 or -higher is recommended (1.14 is known to cause problems 1.16 is known to -work). Either pick up a current version from: - - http://ftp.gnu.org/gnu/bison/ - -or hack around it by inserting the lines: - - #ifdef __GNUC__ - #define alloca __builtin_alloca - #else - #ifdef sparc - #include - #else - char *alloca (); - #endif - #endif - -right after the (100 line!) GNU license comment in bison.simple, remove -grammar.[co] and fire up make again. - -If you use SunOS 4, your kernel must support streams NIT. If you run a -libpcap program and it dies with: - - /dev/nit: No such device - -You must add streams NIT support to your kernel configuration, run -config and boot the new kernel. - -FILES ------ -CHANGES - description of differences between releases -ChmodBPF/* - macOS startup item to set ownership and permissions - on /dev/bpf* -CMakeLists.txt - CMake file -CONTRIBUTING - guidelines for contributing -CREDITS - people that have helped libpcap along -INSTALL.md - this file -LICENSE - the license under which tcpdump is distributed -Makefile.in - compilation rules (input to the configure script) -README.md - description of distribution -doc/README.aix - notes on using libpcap on AIX -doc/README.dag - notes on using libpcap to capture on Endace DAG devices -doc/README.hpux - notes on using libpcap on HP-UX -doc/README.linux.md - notes on using libpcap on Linux -doc/README.macos - notes on using libpcap on macOS -doc/README.septel - notes on using libpcap to capture on Intel/Septel devices -doc/README.sita - notes on using libpcap to capture on SITA devices -doc/README.tru64 - notes on using libpcap on Digital/Tru64 UNIX -doc/README.Win32 - notes on using libpcap on Win32 systems (with Npcap) -VERSION - version of this release -acconfig.h - support for post-2.13 autoconf -aclocal.m4 - autoconf macros -arcnet.h - ARCNET definitions -atmuni31.h - ATM Q.2931 definitions -bpf_dump.c - BPF program printing routines -bpf_filter.c - BPF filtering routines -bpf_image.c - BPF disassembly routine -config.guess - autoconf support -config.h.in - autoconf input -config.sub - autoconf support -configure - configure script (run this first) -configure.ac - configure script source -dlpisubs.c - DLPI-related functions for pcap-dlpi.c and pcap-libdlpi.c -dlpisubs.h - DLPI-related function declarations -etherent.c - /etc/ethers support routines -ethertype.h - Ethernet protocol types and names definitions -fad-getad.c - pcap_findalldevs() for systems with getifaddrs() -fad-gifc.c - pcap_findalldevs() for systems with only SIOCGIFLIST -fad-glifc.c - pcap_findalldevs() for systems with SIOCGLIFCONF -filtertest.c - test program for BPF compiler -findalldevstest.c - test program for pcap_findalldevs() -gencode.c - BPF code generation routines -gencode.h - BPF code generation definitions -grammar.y - filter string grammar -ieee80211.h - 802.11 definitions -install-sh - BSD style install script -lbl/os-*.h - OS-dependent defines and prototypes -llc.h - 802.2 LLC SAP definitions -missing/* - replacements for missing library functions -mkdep - construct Makefile dependency list -msdos/* - drivers for MS-DOS capture support -nametoaddr.c - hostname to address routines -nlpid.h - OSI network layer protocol identifier definitions -net - symlink to bpf/net -optimize.c - BPF optimization routines -pcap/bluetooth.h - public definition of DLT_BLUETOOTH_HCI_H4_WITH_PHDR header -pcap/bpf.h - BPF definitions -pcap/namedb.h - public libpcap name database definitions -pcap/pcap.h - public libpcap definitions -pcap/sll.h - public definition of DLT_LINUX_SLL header -pcap/usb.h - public definition of DLT_USB header -pcap-bpf.c - BSD Packet Filter support -pcap-bpf.h - header for backwards compatibility -pcap-bt-linux.c - Bluetooth capture support for Linux -pcap-bt-linux.h - Bluetooth capture support for Linux -pcap-dag.c - Endace DAG device capture support -pcap-dag.h - Endace DAG device capture support -pcap-dlpi.c - Data Link Provider Interface support -pcap-dos.c - MS-DOS capture support -pcap-dos.h - headers for MS-DOS capture support -pcap-enet.c - enet support -pcap-int.h - internal libpcap definitions -pcap-libdlpi.c - Data Link Provider Interface support for systems with libdlpi -pcap-linux.c - Linux packet socket support -pcap-namedb.h - header for backwards compatibility -pcap-nit.c - SunOS Network Interface Tap support -pcap-nit.h - SunOS Network Interface Tap definitions -pcap-npf.c - WinPcap capture support -pcap-null.c - dummy monitor support (allows offline use of libpcap) -pcap-pf.c - Ultrix and Digital/Tru64 UNIX Packet Filter support -pcap-pf.h - Ultrix and Digital/Tru64 UNIX Packet Filter definitions -pcap-septel.c - Intel/Septel device capture support -pcap-septel.h - Intel/Septel device capture support -pcap-sita.c - SITA device capture support -pcap-sita.h - SITA device capture support -pcap-sita.html - SITA device capture documentation -pcap-stdinc.h - includes and #defines for compiling on Win32 systems -pcap-snit.c - SunOS 4.x STREAMS-based Network Interface Tap support -pcap-snoop.c - IRIX Snoop network monitoring support -pcap-usb-linux.c - USB capture support for Linux -pcap-usb-linux.h - USB capture support for Linux -pcap.3pcap - manual entry for the library -pcap.c - pcap utility routines -pcap.h - header for backwards compatibility -pcap_*.3pcap - manual entries for library functions -pcap-filter.4 - manual entry for filter syntax -pcap-linktype.4 - manual entry for link-layer header types -ppp.h - Point to Point Protocol definitions -savefile.c - offline support -scanner.l - filter string scanner -sunatmpos.h - definitions for SunATM capturing -Win32 - headers and routines for building on Win32 systems +## Description of files + CHANGES - description of differences between releases + ChmodBPF/* - macOS startup item to set ownership and permissions on /dev/bpf* + CMakeLists.txt - CMake file + CONTRIBUTING.md - guidelines for contributing + CREDITS - people that have helped libpcap along + INSTALL.md - this file + LICENSE - the license under which tcpdump is distributed + Makefile.in - compilation rules (input to the configure script) + README.md - description of distribution + doc/README.aix - notes on using libpcap on AIX + doc/README.dag - notes on using libpcap to capture on Endace DAG devices + doc/README.hpux - notes on using libpcap on HP-UX + doc/README.linux - notes on using libpcap on Linux + doc/README.macos - notes on using libpcap on macOS + doc/README.septel - notes on using libpcap to capture on Intel/Septel devices + doc/README.sita - notes on using libpcap to capture on SITA devices + doc/README.solaris.md - notes on using libpcap on Solaris + doc/README.Win32.md - notes on using libpcap on Win32 systems (with Npcap) + VERSION - version of this release + aclocal.m4 - autoconf macros + arcnet.h - ARCNET definitions + atmuni31.h - ATM Q.2931 definitions + bpf_dump.c - BPF program printing routines + bpf_filter.c - BPF filtering routines + bpf_image.c - BPF disassembly routine + config.guess - autoconf support + config.h.in - autoconf input + config.sub - autoconf support + configure - configure script (run this first) + configure.ac - configure script source + dlpisubs.c - DLPI-related functions for pcap-dlpi.c and pcap-libdlpi.c + dlpisubs.h - DLPI-related function declarations + etherent.c - /etc/ethers support routines + ethertype.h - Ethernet protocol types and names definitions + fad-getad.c - pcap_findalldevs() for systems with getifaddrs() + fad-gifc.c - pcap_findalldevs() for systems with only SIOCGIFLIST + fad-glifc.c - pcap_findalldevs() for systems with SIOCGLIFCONF + testprogs/filtertest.c - test program for BPF compiler + testprogs/findalldevstest.c - test program for pcap_findalldevs() + gencode.c - BPF code generation routines + gencode.h - BPF code generation definitions + grammar.y - filter string grammar + ieee80211.h - 802.11 definitions + install-sh - BSD style install script + lbl/os-*.h - OS-dependent defines and prototypes + llc.h - 802.2 LLC SAP definitions + missing/* - replacements for missing library functions + mkdep - construct Makefile dependency list + msdos/* - drivers for MS-DOS capture support + nametoaddr.c - hostname to address routines + nlpid.h - OSI network layer protocol identifier definitions + optimize.c - BPF optimization routines + pcap/bluetooth.h - public definition of DLT_BLUETOOTH_HCI_H4_WITH_PHDR header + pcap/bpf.h - BPF definitions + pcap/namedb.h - public libpcap name database definitions + pcap/pcap.h - public libpcap definitions + pcap/sll.h - public definitions of DLT_LINUX_SLL and DLT_LINUX_SLL2 headers + pcap/usb.h - public definition of DLT_USB header + pcap-bpf.c - BSD Packet Filter support + pcap-bpf.h - header for backwards compatibility + pcap-bt-linux.c - Bluetooth capture support for Linux + pcap-bt-linux.h - Bluetooth capture support for Linux + pcap-dag.c - Endace DAG device capture support + pcap-dag.h - Endace DAG device capture support + pcap-dlpi.c - Data Link Provider Interface support + pcap-dos.c - MS-DOS capture support + pcap-dos.h - headers for MS-DOS capture support + pcap-enet.c - enet support + pcap-int.h - internal libpcap definitions + pcap-libdlpi.c - Data Link Provider Interface support for systems with libdlpi + pcap-linux.c - Linux packet socket support + pcap-namedb.h - header for backwards compatibility + pcap-nit.c - SunOS Network Interface Tap support + pcap-npf.c - Npcap capture support + pcap-null.c - dummy monitor support (allows offline use of libpcap) + pcap-pf.c - Ultrix and Digital/Tru64 UNIX Packet Filter support + pcap-septel.c - Intel/Septel device capture support + pcap-septel.h - Intel/Septel device capture support + pcap-sita.c - SITA device capture support + pcap-sita.h - SITA device capture support + pcap-sita.html - SITA device capture documentation + pcap-snit.c - SunOS 4.x STREAMS-based Network Interface Tap support + pcap-snoop.c - IRIX Snoop network monitoring support + pcap-usb-linux.c - USB capture support for Linux + pcap-usb-linux.h - USB capture support for Linux + pcap.3pcap - manual entry for the library + pcap.c - pcap utility routines + pcap.h - header for backwards compatibility + pcap_*.3pcap - manual entries for library functions + pcap-filter.manmisc.in - manual entry for filter syntax + pcap-linktype.manmisc.in - manual entry for link-layer header types + ppp.h - Point to Point Protocol definitions + savefile.c - offline support + scanner.l - filter string scanner + sunatmpos.h - definitions for SunATM capturing diff --git a/Makefile-devel-adds b/Makefile-devel-adds index fea63bbce3a7..7cfd6c91d8e0 100644 --- a/Makefile-devel-adds +++ b/Makefile-devel-adds @@ -3,12 +3,12 @@ # From autoconf.info . Works best with GNU Make. # ${srcdir}/configure: configure.ac aclocal.m4 - cd ${srcdir} && autoconf + (cd ${srcdir} && autoconf) # autoheader might not change config.h.in, so touch a stamp file. ${srcdir}/config.h.in: ${srcdir}/stamp-h.in ${srcdir}/stamp-h.in: configure.ac aclocal.m4 - cd ${srcdir} && autoheader + (cd ${srcdir} && autoheader) echo timestamp > ${srcdir}/stamp-h.in config.h: stamp-h diff --git a/Makefile.in b/Makefile.in index 5a6b165da3f0..54246586828d 100644 --- a/Makefile.in +++ b/Makefile.in @@ -38,6 +38,7 @@ mandir = @mandir@ # VPATH srcdir = @srcdir@ +top_srcdir = @top_srcdir@ VPATH = @srcdir@ # @@ -60,16 +61,16 @@ CROSSFLAGS= CFLAGS = @CFLAGS@ ${CROSSFLAGS} LDFLAGS = @LDFLAGS@ ${CROSSFLAGS} DYEXT = @DYEXT@ -V_RPATH_OPT = @V_RPATH_OPT@ +RPATH = @RPATH@ DEPENDENCY_CFLAG = @DEPENDENCY_CFLAG@ PROG=libpcap PTHREAD_LIBS=@PTHREAD_LIBS@ BUILD_RPCAPD=@BUILD_RPCAPD@ INSTALL_RPCAPD=@INSTALL_RPCAPD@ -EXTRA_NETWORK_LIBS=@EXTRA_NETWORK_LIBS@ # Standard CFLAGS for building members of a shared library FULL_CFLAGS = $(CCOPT) @V_LIB_CCOPT_FAT@ $(SHLIB_CCOPT) $(INCLS) $(DEFS) $(CFLAGS) +CXXFLAGS = $(CCOPT) @V_LIB_CCOPT_FAT@ $(SHLIB_CCOPT) $(INCLS) $(DEFS) $(CFLAGS) INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ @@ -77,7 +78,7 @@ INSTALL_DATA = @INSTALL_DATA@ RANLIB = @RANLIB@ LEX = @LEX@ -YACC = @YACC@ +BISON_BYACC = @BISON_BYACC@ # Explicitly define compilation rule since SunOS 4's make doesn't like gcc. # Also, gcc does not remove the .o before forking 'as', which can be a @@ -86,40 +87,48 @@ YACC = @YACC@ @rm -f $@ $(CC) $(FULL_CFLAGS) -c $(srcdir)/$*.c -PSRC = pcap-@V_PCAP@.c @USB_SRC@ @BT_SRC@ @BT_MONITOR_SRC@ @NETFILTER_SRC@ @DBUS_SRC@ @NETMAP_SRC@ @RDMA_SRC@ -FSRC = @V_FINDALLDEVS@ -SSRC = @SSRC@ -CSRC = pcap.c gencode.c optimize.c nametoaddr.c etherent.c \ - fmtutils.c \ - savefile.c sf-pcap.c sf-pcapng.c pcap-common.c \ - bpf_image.c bpf_filter.c bpf_dump.c -GENSRC = scanner.c grammar.c +PLATFORM_C_SRC = @PLATFORM_C_SRC@ +PLATFORM_CXX_SRC = @PLATFORM_CXX_SRC@ +MODULE_C_SRC = @MODULE_C_SRC@ +REMOTE_C_SRC = @REMOTE_C_SRC@ +COMMON_C_SRC = pcap.c gencode.c optimize.c nametoaddr.c etherent.c \ + fmtutils.c pcap-util.c \ + savefile.c sf-pcap.c sf-pcapng.c pcap-common.c \ + pcap-usb-linux-common.c bpf_image.c bpf_filter.c bpf_dump.c +GENERATED_C_SRC = scanner.c grammar.c LIBOBJS = @LIBOBJS@ -SRC = $(PSRC) $(FSRC) $(CSRC) $(SSRC) $(GENSRC) +SRC = $(PLATFORM_C_SRC) $(PLATFORM_CXX_SRC) \ + $(MODULE_C_SRC) $(REMOTE_C_SRC) $(COMMON_C_SRC) \ + $(GENERATED_C_SRC) # We would like to say "OBJ = $(SRC:.c=.o)" but Ultrix's make cannot -# hack the extra indirection -OBJ = $(PSRC:.c=.o) $(FSRC:.c=.o) $(CSRC:.c=.o) $(SSRC:.c=.o) $(GENSRC:.c=.o) $(LIBOBJS) +# hack the extra indirection, and we have to handle PLATFORM_CXX_SRC +# differently from the defines for C source +OBJ = $(PLATFORM_C_SRC:.c=.o) $(PLATFORM_CXX_SRC:.cpp=.o) \ + $(MODULE_C_SRC:.c=.o) $(REMOTE_C_SRC:.c=.o) $(COMMON_C_SRC:.c=.o) \ + $(GENERATED_C_SRC:.c=.o) \ + $(LIBOBJS) + PUBHDR = \ pcap.h \ pcap-bpf.h \ pcap-namedb.h \ - pcap/bpf.h \ pcap/bluetooth.h \ + pcap/bpf.h \ pcap/can_socketcan.h \ pcap/compiler-tests.h \ pcap/dlt.h \ pcap/funcattrs.h \ - pcap/pcap-inttypes.h \ pcap/ipnet.h \ pcap/namedb.h \ pcap/nflog.h \ + pcap/pcap-inttypes.h \ pcap/pcap.h \ pcap/sll.h \ pcap/socket.h \ - pcap/vlan.h \ - pcap/usb.h + pcap/usb.h \ + pcap/vlan.h HDR = $(PUBHDR) \ arcnet.h \ @@ -139,6 +148,9 @@ HDR = $(PUBHDR) \ pcap-int.h \ pcap-rpcap.h \ pcap-types.h \ + pcap-usb-linux-common.h \ + pcap-util.h \ + pflog.h \ portability.h \ ppp.h \ rpcap-protocol.h \ @@ -154,7 +166,7 @@ TAGFILES = \ $(SRC) $(HDR) CLEANFILES = $(OBJ) libpcap.a libpcap.so.`cat $(srcdir)/VERSION` \ - $(PROG)-`cat $(srcdir)/VERSION`.tar.gz $(GENSRC) $(GENHDR) \ + $(PROG)-`cat $(srcdir)/VERSION`.tar.gz $(GENERATED_C_SRC) $(GENHDR) \ lex.yy.c pcap-config libpcap.pc MAN1 = pcap-config.1 @@ -193,6 +205,7 @@ MAN3PCAP_NOEXPAND = \ pcap_get_required_select_timeout.3pcap \ pcap_get_selectable_fd.3pcap \ pcap_geterr.3pcap \ + pcap_init.3pcap \ pcap_inject.3pcap \ pcap_is_swapped.3pcap \ pcap_lib_version.3pcap \ @@ -241,15 +254,27 @@ EXTRA_DIST = \ Makefile.in \ Makefile-devel-adds \ README.md \ - doc \ + doc/README.Win32.md \ + doc/README.aix \ + doc/README.dag \ + doc/README.hpux \ + doc/README.linux \ + doc/README.macos \ + doc/README.septel \ + doc/README.sita \ + doc/README.solaris.md \ CONTRIBUTING.md \ TODO \ VERSION \ aclocal.m4 \ + charconv.c \ + charconv.h \ chmod_bpf \ cmake_uninstall.cmake.in \ cmakeconfig.h.in \ + cmake/Modules/FindAirPcap.cmake \ cmake/Modules/FindDAG.cmake \ + cmake/Modules/Finddpdk.cmake \ cmake/Modules/FindFseeko.cmake \ cmake/Modules/FindLFS.cmake \ cmake/Modules/FindPacket.cmake \ @@ -266,7 +291,7 @@ EXTRA_DIST = \ fad-getad.c \ fad-gifc.c \ fad-glifc.c \ - grammar.y \ + grammar.y.in \ install-sh \ lbl/os-aix4.h \ lbl/os-aix7.h \ @@ -280,12 +305,10 @@ EXTRA_DIST = \ missing/asprintf.c \ missing/getopt.c \ missing/getopt.h \ - missing/snprintf.c \ missing/strlcat.c \ missing/strlcpy.c \ missing/strtok_r.c \ missing/win_asprintf.c \ - missing/win_snprintf.c \ mkdep \ msdos/bin2c.c \ msdos/makefile \ @@ -298,6 +321,8 @@ EXTRA_DIST = \ msdos/readme.dos \ nomkdep \ org.tcpdump.chmod_bpf.plist \ + pcap-airpcap.c \ + pcap-airpcap.h \ pcap-bpf.c \ pcap-bt-linux.c \ pcap-bt-linux.h \ @@ -312,7 +337,10 @@ EXTRA_DIST = \ pcap-dlpi.c \ pcap-dos.c \ pcap-dos.h \ + pcap-dpdk.c \ + pcap-dpdk.h \ pcap-enet.c \ + pcap-haiku.cpp \ pcap-int.h \ pcap-libdlpi.c \ pcap-linux.c \ @@ -367,6 +395,8 @@ EXTRA_DIST = \ rpcapd/win32-svc.h \ sockutils.c \ sockutils.h \ + sslutils.c \ + sslutils.h \ scanner.l \ testprogs/CMakeLists.txt \ testprogs/Makefile.in \ @@ -374,16 +404,30 @@ EXTRA_DIST = \ testprogs/capturetest.c \ testprogs/filtertest.c \ testprogs/findalldevstest.c \ + testprogs/findalldevstest-perf.c \ + testprogs/fuzz/CMakeLists.txt \ + testprogs/fuzz/fuzz_both.c \ + testprogs/fuzz/fuzz_both.options \ + testprogs/fuzz/fuzz_filter.c \ + testprogs/fuzz/fuzz_filter.options \ + testprogs/fuzz/fuzz_pcap.c \ + testprogs/fuzz/fuzz_pcap.options \ + testprogs/fuzz/onefile.c \ + testprogs/nonblocktest.c \ testprogs/opentest.c \ testprogs/reactivatetest.c \ testprogs/selpolltest.c \ testprogs/threadsignaltest.c \ testprogs/unix.h \ testprogs/valgrindtest.c \ - tests/shb-option-too-long.pcapng \ - Win32/Prj/wpcap.sln \ - Win32/Prj/wpcap.vcxproj \ - Win32/Prj/wpcap.vcxproj.filters + testprogs/visopts.py \ + testprogs/writecaptest.c + +TEST_DIST = `git ls-files tests | grep -v 'tests/\..*'` + +RELEASE_FILES = $(COMMON_C_SRC) $(HDR) $(MAN1) $(MAN3PCAP_EXPAND) \ + $(MAN3PCAP_NOEXPAND) $(MANFILE) $(MANMISC) $(EXTRA_DIST) \ + $(TEST_DIST) all: libpcap.a shared $(BUILD_RPCAPD) libpcap.pc pcap-config @@ -480,8 +524,27 @@ scanner.h: scanner.c scanner.o: scanner.c grammar.h $(CC) $(FULL_CFLAGS) -c scanner.c -grammar.c: $(srcdir)/grammar.y - $(YACC) -p pcap_ -o grammar.c -d $< +# +# Generate the grammar.y file. +# +# Some Makes, e.g. AIX Make and Solaris Make, can't handle "--file=$@.tmp:$<"; +# for example, the Solaris 9 make man page says +# +# Because make assigns $< and $* as it would for implicit rules +# (according to the suffixes list and the directory contents), +# they may be unreliable when used within explicit target entries. +# +# and this is an explicit target entry. +# +# Therefore, instead of using $<, we explicitly put in $(srcdir)/libpcap.pc.in. +# +grammar.y: $(srcdir)/grammar.y.in ./config.status + @rm -f $@ $@.tmp + ./config.status --file=$@.tmp:$(srcdir)/grammar.y.in + mv $@.tmp $@ + +grammar.c: grammar.y + $(BISON_BYACC) -p pcap_ -o grammar.c -d $< grammar.h: grammar.c ## Recover from the removal of $@ @if test -f $@; then :; else \ @@ -528,7 +591,6 @@ libpcap.pc: $(srcdir)/libpcap.pc.in ./config.status @rm -f $@ $@.tmp ./config.status --file=$@.tmp:$(srcdir)/libpcap.pc.in mv $@.tmp $@ - chmod a+x $@ # # Generate the pcap-config script. See above. @@ -543,13 +605,13 @@ pcap-config: $(srcdir)/pcap-config.in ./config.status # Remote pcap daemon. # build-rpcapd: libpcap.a - cd rpcapd; $(MAKE) + (cd rpcapd; $(MAKE)) # # Test programs - not built by default, and not installed. # testprogs: FORCE - cd testprogs; $(MAKE) + (cd testprogs; $(MAKE)) FORCE: @@ -681,7 +743,7 @@ install-archive-shareda: # install-rpcapd: - cd rpcapd; $(MAKE) DESTDIR=$(DESTDIR) install + (cd rpcapd; $(MAKE) DESTDIR=$(DESTDIR) install) uninstall: uninstall-shared uninstall-rpcapd rm -f $(DESTDIR)$(libdir)/libpcap.a @@ -695,6 +757,7 @@ uninstall: uninstall-shared uninstall-rpcapd for i in $(MAN3PCAP); do \ rm -f $(DESTDIR)$(mandir)/man3/$$i; done rm -f $(DESTDIR)$(mandir)/man3/pcap_datalink_val_to_description.3pcap + rm -f $(DESTDIR)$(mandir)/man3/pcap_datalink_val_to_description_or_dlt.3pcap rm -f $(DESTDIR)$(mandir)/man3/pcap_dump_fopen.3pcap rm -f $(DESTDIR)$(mandir)/man3/pcap_freealldevs.3pcap rm -f $(DESTDIR)$(mandir)/man3/pcap_perror.3pcap @@ -737,21 +800,21 @@ uninstall-shared-shareda: uninstall-shared-none: uninstall-rpcapd: - cd rpcapd; $(MAKE) DESTDIR=$(DESTDIR) uninstall + (cd rpcapd; $(MAKE) DESTDIR=$(DESTDIR) uninstall) clean: rm -f $(CLEANFILES) - cd rpcapd; $(MAKE) clean - cd testprogs; $(MAKE) clean + (cd rpcapd; $(MAKE) clean) + (cd testprogs; $(MAKE) clean) distclean: clean - rm -f Makefile config.cache config.log config.status \ - config.h gnuc.h net os-proto.h libpcap.pc \ - pcap-config stamp-h stamp-h.in + rm -f Makefile grammar.y config.cache config.log config.status \ + config.h config.h.in~ configure~ configure.ac~ \ + gnuc.h net os-proto.h libpcap.pc pcap-config stamp-h stamp-h.in rm -f $(MAN3PCAP_EXPAND:.in=) $(MANFILE:.in=) $(MANMISC:.in=) rm -rf autom4te.cache - cd rpcapd; $(MAKE) distclean - cd testprogs; $(MAKE) distclean + (cd rpcapd; $(MAKE) distclean) + (cd testprogs; $(MAKE) distclean) extags: $(TAGFILES) ctags $(TAGFILES) @@ -760,15 +823,21 @@ tags: $(TAGFILES) ctags -wtd $(TAGFILES) releasetar: - @cwd=`pwd` ; dir=`basename $$cwd` ; name=$(PROG)-`cat VERSION` ; \ - mkdir $$name; \ - tar -c --exclude='*~' -f - $(CSRC) $(HDR) $(MAN1) $(MAN3PCAP_EXPAND) \ - $(MAN3PCAP_NOEXPAND) $(MANFILE) $(MANMISC) $(EXTRA_DIST) | \ - (cd $$name; tar xf -); \ - tar -c -z -f $$name.tar.gz $$name; \ - rm -rf $$name + @TAG=$(PROG)-`cat VERSION` && \ + if git show-ref --tags --quiet --verify -- "refs/tags/$$TAG"; then \ + git archive --prefix="$$TAG"/ -o "$$TAG".tar.gz "$$TAG" \ + $(RELEASE_FILES) && \ + echo "Archive build from tag $$TAG."; \ + else \ + git archive --prefix="$$TAG"/ -o "$$TAG".tar.gz HEAD \ + $(RELEASE_FILES) && \ + echo "No $$TAG tag. Archive build from HEAD."; \ + fi -depend: $(GENSRC) $(GENHDR) - $(MKDEP) -c "$(CC)" -m "$(DEPENDENCY_CFLAG)" $(CFLAGS) $(DEFS) $(INCLS) $(SRC) - cd rpcapd; $(MAKE) depend - cd testprogs; $(MAKE) depend +depend: $(GENERATED_C_SRC) $(GENHDR) + $(MKDEP) -c "$(CC)" -m "$(DEPENDENCY_CFLAG)" -s "$(srcdir)" $(CFLAGS) $(DEFS) $(INCLS) $(SRC) + (cd rpcapd; $(MAKE) depend) + (cd testprogs; $(MAKE) depend) + +shellcheck: + shellcheck -f gcc -e SC2006 build.sh build_matrix.sh build_common.sh diff --git a/README.md b/README.md index 78cc3c4b41b5..46c33c24125e 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,17 @@ -To report a security issue please send an e-mail to security@tcpdump.org. +# LIBPCAP 1.x.y by [The Tcpdump Group](https://www.tcpdump.org) + +**To report a security issue please send an e-mail to security@tcpdump.org.** To report bugs and other problems, contribute patches, request a -feature, provide generic feedback etc please see the file -[CONTRIBUTING](CONTRIBUTING.md) in the libpcap source tree root. +feature, provide generic feedback etc please see the +[guidelines for contributing](CONTRIBUTING.md). -The directory doc/ has README files about specific operating systems and -options. - -LIBPCAP 1.x.y -Now maintained by "The Tcpdump Group" -https://www.tcpdump.org +The [documentation directory](doc/) has README files about specific +operating systems and options. Anonymous Git is available via: - https://github.com/the-tcpdump-group/libpcap.git -formerly from Lawrence Berkeley National Laboratory - Network Research Group - ftp://ftp.ee.lbl.gov/old/libpcap-0.4a7.tar.Z + https://github.com/the-tcpdump-group/libpcap.git This directory contains source code for libpcap, a system-independent interface for user-level packet capture. libpcap provides a portable @@ -28,7 +23,14 @@ require this functionality, we've created this system-independent API to ease in porting and to alleviate the need for several system-dependent packet capture modules in each application. -For some platforms there are README.{system} files that discuss issues +```text +formerly from Lawrence Berkeley National Laboratory + Network Research Group + ftp://ftp.ee.lbl.gov/old/libpcap-0.4a7.tar.Z +``` + +### Support for particular platforms and BPF +For some platforms there are `README.{system}` files that discuss issues with the OS's interface for packet capture on those platforms, such as how to enable support for that interface in the OS, if it's not built in by default. @@ -36,22 +38,10 @@ by default. The libpcap interface supports a filtering mechanism based on the architecture in the BSD packet filter. BPF is described in the 1993 Winter Usenix paper ``The BSD Packet Filter: A New Architecture for -User-level Packet Capture''. A compressed PostScript version can be -found at - - ftp://ftp.ee.lbl.gov/papers/bpf-usenix93.ps.Z - -or - - https://www.tcpdump.org/papers/bpf-usenix93.ps.Z - -and a gzipped version can be found at - - https://www.tcpdump.org/papers/bpf-usenix93.ps.gz - -A PDF version can be found at - - https://www.tcpdump.org/papers/bpf-usenix93.pdf +User-level Packet Capture'' +([compressed PostScript](https://www.tcpdump.org/papers/bpf-usenix93.ps.Z), +[gzipped PostScript](https://www.tcpdump.org/papers/bpf-usenix93.ps.gz), +[PDF](https://www.tcpdump.org/papers/bpf-usenix93.pdf)). Although most packet capture interfaces support in-kernel filtering, libpcap utilizes in-kernel filtering only for the BPF interface. @@ -62,32 +52,25 @@ would translate BPF filters into a filter program that is compatible with the underlying kernel subsystem, but this is not yet implemented. BPF is standard in 4.4BSD, BSD/OS, NetBSD, FreeBSD, OpenBSD, DragonFly -BSD, and macOS; an older, modified and undocumented version is standard -in AIX. {DEC OSF/1, Digital UNIX, Tru64 UNIX} uses the packetfilter -interface but has been extended to accept BPF filters (which libpcap -utilizes). Also, you can add BPF filter support to Ultrix using the -kernel source and/or object patches available in: - - https://www.tcpdump.org/other/bpfext42.tar.Z +BSD, macOS, and Solaris 11; an older, modified and undocumented version +is standard in AIX. {DEC OSF/1, Digital UNIX, Tru64 UNIX} uses the +packetfilter interface but has been extended to accept BPF filters +(which libpcap utilizes). Linux has a number of BPF based systems, and libpcap does not support any of the eBPF mechanisms as yet, although it supports many of the memory mapped receive mechanisms. -See the [README.linux](doc/README.linux.md) file for more information. +See the [Linux-specific README](doc/README.linux) for more information. -Note to Linux distributions and *BSD systems that include libpcap: +### Note to Linux distributions and *BSD systems that include libpcap: There's now a rule to make a shared library, which should work on Linux and *BSD, among other platforms. -It sets the soname of the library to "libpcap.so.1"; this is what it -should be, *NOT* libpcap.so.1.x or libpcap.so.1.x.y or something such as +It sets the soname of the library to `libpcap.so.1`; this is what it +should be, **NOT** `libpcap.so.1.x` or `libpcap.so.1.x.y` or something such as that. We've been maintaining binary compatibility between libpcap releases for quite a while; there's no reason to tie a binary linked with libpcap to a particular release of libpcap. - -Current versions can be found at https://www.tcpdump.org. - - - The TCPdump group diff --git a/TODO b/TODO index aae24c22d251..65e166b60bc1 100644 --- a/TODO +++ b/TODO @@ -31,5 +31,3 @@ Less urgent items + too many functions. There are a lot of functions for everything which violates the KISS principle. Why do we need pcap_strerror, pcap_perror and pcap_geterr? - + the manpage has a brief description of each function but where is the - big picture? Seems like you need to buy UNP for that... diff --git a/VERSION b/VERSION index 9ab8337f3962..587c5f0c7309 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.9.1 +1.10.3 diff --git a/Win32/Prj/wpcap.sln b/Win32/Prj/wpcap.sln deleted file mode 100644 index 5a9fce98ee35..000000000000 --- a/Win32/Prj/wpcap.sln +++ /dev/null @@ -1,28 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.40629.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wpcap", "wpcap.vcxproj", "{8E92D840-6A36-452A-A13C-6E1BA5A2C5A9}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Debug|x64 = Debug|x64 - Release|Win32 = Release|Win32 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {8E92D840-6A36-452A-A13C-6E1BA5A2C5A9}.Debug|Win32.ActiveCfg = Debug|Win32 - {8E92D840-6A36-452A-A13C-6E1BA5A2C5A9}.Debug|Win32.Build.0 = Debug|Win32 - {8E92D840-6A36-452A-A13C-6E1BA5A2C5A9}.Debug|x64.ActiveCfg = Debug|x64 - {8E92D840-6A36-452A-A13C-6E1BA5A2C5A9}.Debug|x64.Build.0 = Debug|x64 - {8E92D840-6A36-452A-A13C-6E1BA5A2C5A9}.Release|Win32.ActiveCfg = Release|Win32 - {8E92D840-6A36-452A-A13C-6E1BA5A2C5A9}.Release|Win32.Build.0 = Release|Win32 - {8E92D840-6A36-452A-A13C-6E1BA5A2C5A9}.Release|x64.ActiveCfg = Release|x64 - {8E92D840-6A36-452A-A13C-6E1BA5A2C5A9}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/Win32/Prj/wpcap.vcxproj b/Win32/Prj/wpcap.vcxproj deleted file mode 100644 index 43b7099b7707..000000000000 --- a/Win32/Prj/wpcap.vcxproj +++ /dev/null @@ -1,233 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - - - {8E92D840-6A36-452A-A13C-6E1BA5A2C5A9} - - - - DynamicLibrary - v120 - false - MultiByte - - - DynamicLibrary - v120 - false - MultiByte - - - DynamicLibrary - v120 - false - MultiByte - true - - - DynamicLibrary - v120 - false - MultiByte - true - - - - - - - - - - - - - - - - - - - - - - - .\Release\ - .\Release\ - false - ../../../;$(IncludePath) - - - false - ../../../;$(IncludePath) - - - .\Debug\ - .\Debug\ - true - ../../../;$(IncludePath) - - - true - ../../../;$(IncludePath) - - - - MultiThreaded - Default - true - true - MaxSpeed - true - Level3 - ../../;../../lbl/;../../bpf/;../include/;../../../../common;../../../../dag/include;../../../../dag/drv/windows;../../../Win32-Extensions;./;Win32-Extensions;%(AdditionalIncludeDirectories) - HAVE_VERSION_H;__STDC_VERSION__=199901L;HAVE_PACKET_IS_LOOPBACK_ADAPTER;NDEBUG;YY_NEVER_INTERACTIVE;_USRDLL;pcap_EXPORTS;HAVE_STRERROR;__STDC__;INET6;_WINDOWS;ENABLE_REMOTE;WIN32;_U_=;YY_NO_UNISTD_H;%(PreprocessorDefinitions) - - - 0x0409 - NDEBUG;%(PreprocessorDefinitions) - - - ws2_32.lib;..\..\..\..\packetWin7\Dll\Project\Release No NetMon and AirPcap\Packet.lib;%(AdditionalDependencies) - - - call ..\..\GenVersion.bat ..\..\VERSION ..\..\pcap_version.h.in ..\..\pcap_version.h -win_flex -Ppcap_ -7 --outfile=..\..\scanner.c --header-file=..\..\scanner.h ..\..\scanner.l -win_bison -ppcap_ --yacc --output=..\..\grammar.c --defines ..\..\grammar.y - - - - - MultiThreaded - Default - true - true - MaxSpeed - true - Level3 - ../../;../../lbl/;../../bpf/;../include/;../../../../common;../../../../dag/include;../../../../dag/drv/windows;../../../Win32-Extensions;./;Win32-Extensions;%(AdditionalIncludeDirectories) - HAVE_VERSION_H;__STDC_VERSION__=199901L;HAVE_PACKET_IS_LOOPBACK_ADAPTER;NDEBUG;YY_NEVER_INTERACTIVE;_USRDLL;pcap_EXPORTS;HAVE_STRERROR;__STDC__;INET6;_WINDOWS;ENABLE_REMOTE;WIN32;_U_=;YY_NO_UNISTD_H;%(PreprocessorDefinitions) - - - 0x0409 - NDEBUG;%(PreprocessorDefinitions) - - - ws2_32.lib;..\..\..\..\packetWin7\Dll\Project\x64\Release No NetMon and AirPcap\Packet.lib;%(AdditionalDependencies) - - - call ..\..\GenVersion.bat ..\..\VERSION ..\..\pcap_version.h.in ..\..\pcap_version.h -win_flex -Ppcap_ -7 --outfile=..\..\scanner.c --header-file=..\..\scanner.h ..\..\scanner.l -win_bison -ppcap_ --yacc --output=..\..\grammar.c --defines ..\..\grammar.y - - - - - MultiThreadedDebug - Default - false - Disabled - true - Level3 - true - EditAndContinue - ../../;../../lbl/;../../bpf/;../include/;../../../../common;../../../../dag/include;../../../../dag/drv/windows;../../../Win32-Extensions;./;Win32-Extensions;%(AdditionalIncludeDirectories) - HAVE_VERSION_H;__STDC_VERSION__=199901L;HAVE_PACKET_IS_LOOPBACK_ADAPTER;_DEBUG;YY_NEVER_INTERACTIVE;_USRDLL;pcap_EXPORTS;HAVE_STRERROR;__STDC__;INET6;_WINDOWS;ENABLE_REMOTE;WIN32;_U_=;YY_NO_UNISTD_H;%(PreprocessorDefinitions) - EnableFastChecks - - - 0x0409 - _DEBUG;%(PreprocessorDefinitions) - - - ws2_32.lib;..\..\..\..\packetWin7\Dll\Project\Release No NetMon and AirPcap\Packet.lib;%(AdditionalDependencies) - - - call ..\..\GenVersion.bat ..\..\VERSION ..\..\pcap_version.h.in ..\..\pcap_version.h -win_flex -Ppcap_ -7 --outfile=..\..\scanner.c --header-file=..\..\scanner.h ..\..\scanner.l -win_bison -ppcap_ --yacc --output=..\..\grammar.c --defines ..\..\grammar.y - - - - - MultiThreadedDebug - Default - false - Disabled - true - Level3 - ProgramDatabase - ../../;../../lbl/;../../bpf/;../include/;../../../../common;../../../../dag/include;../../../../dag/drv/windows;../../../Win32-Extensions;./;Win32-Extensions;%(AdditionalIncludeDirectories) - HAVE_VERSION_H;__STDC_VERSION__=199901L;HAVE_PACKET_IS_LOOPBACK_ADAPTER;_DEBUG;YY_NEVER_INTERACTIVE;_USRDLL;pcap_EXPORTS;HAVE_STRERROR;__STDC__;INET6;_WINDOWS;ENABLE_REMOTE;WIN32;_U_=;YY_NO_UNISTD_H;%(PreprocessorDefinitions) - EnableFastChecks - - - 0x0409 - _DEBUG;%(PreprocessorDefinitions) - - - ws2_32.lib;..\..\..\..\packetWin7\Dll\Project\x64\Release No NetMon and AirPcap\Packet.lib;%(AdditionalDependencies) - - - call ..\..\GenVersion.bat ..\..\VERSION ..\..\pcap_version.h.in ..\..\pcap_version.h -win_flex -Ppcap_ -7 --outfile=..\..\scanner.c --header-file=..\..\scanner.h ..\..\scanner.l -win_bison -ppcap_ --yacc --output=..\..\grammar.c --defines ..\..\grammar.y - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Win32/Prj/wpcap.vcxproj.filters b/Win32/Prj/wpcap.vcxproj.filters deleted file mode 100644 index 6e06ccbce11d..000000000000 --- a/Win32/Prj/wpcap.vcxproj.filters +++ /dev/null @@ -1,107 +0,0 @@ - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - {c51dce5e-0da9-4e33-a235-d5c76c76485c} - - - {5ec9fd4b-10b5-4527-b249-56b53d844fb1} - - - {c90886f0-8973-436b-a7a1-b9e881544f9a} - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - Resource Files - - - diff --git a/aclocal.m4 b/aclocal.m4 index aa91e846ed6b..502a3711f677 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -232,34 +232,27 @@ AC_DEFUN(AC_LBL_C_INIT, ]) dnl -dnl Check whether, if you pass an unknown warning option to the -dnl compiler, it fails or just prints a warning message and succeeds. -dnl Set ac_lbl_unknown_warning_option_error to the appropriate flag -dnl to force an error if it would otherwise just print a warning message -dnl and succeed. +dnl Save the values of various variables that affect compilation and +dnl linking, and that we don't ourselves modify persistently; done +dnl before a test involving compiling or linking is done, so that we +dnl can restore those variables after the test is done. dnl -AC_DEFUN(AC_LBL_CHECK_UNKNOWN_WARNING_OPTION_ERROR, - [ - AC_MSG_CHECKING([whether the compiler fails when given an unknown warning option]) +AC_DEFUN(AC_LBL_SAVE_CHECK_STATE, +[ save_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS -Wxyzzy-this-will-never-succeed-xyzzy" - AC_TRY_COMPILE( - [], - [return 0], - [ - AC_MSG_RESULT([no]) - # - # We're assuming this is clang, where - # -Werror=unknown-warning-option is the appropriate - # option to force the compiler to fail. - # - ac_lbl_unknown_warning_option_error="-Werror=unknown-warning-option" - ], - [ - AC_MSG_RESULT([yes]) - ]) + save_LIBS="$LIBS" + save_LDFLAGS="$LDFLAGS" +]) + +dnl +dnl Restore the values of variables saved by AC_LBL_SAVE_CHECK_STATE. +dnl +AC_DEFUN(AC_LBL_RESTORE_CHECK_STATE, +[ CFLAGS="$save_CFLAGS" - ]) + LIBS="$save_LIBS" + LDFLAGS="$save_LDFLAGS" +]) dnl dnl Check whether the compiler option specified as the second argument @@ -271,28 +264,42 @@ dnl with the flag in question, and the "treat warnings as errors" flag dnl set, and don't add the flag to the first argument if the compile dnl fails; this is for warning options cause problems that can't be dnl worked around. If a third argument is supplied, a fourth argument -dnl should also be supplied; it's a message desribing what the test +dnl should also be supplied; it's a message describing what the test dnl program is checking. dnl AC_DEFUN(AC_LBL_CHECK_COMPILER_OPT, [ AC_MSG_CHECKING([whether the compiler supports the $2 option]) save_CFLAGS="$CFLAGS" - if expr "x$2" : "x-W.*" >/dev/null - then - CFLAGS="$CFLAGS $ac_lbl_unknown_warning_option_error $2" - elif expr "x$2" : "x-f.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror $2" - elif expr "x$2" : "x-m.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror $2" - else - CFLAGS="$CFLAGS $2" - fi - AC_TRY_COMPILE( - [], - [return 0], + CFLAGS="$CFLAGS $2" + # + # XXX - yes, this depends on the way AC_LANG_WERROR works, + # but no mechanism is provided to turn AC_LANG_WERROR on + # *and then turn it back off*, so that we *only* do it when + # testing compiler options - 15 years after somebody asked + # for it: + # + # https://autoconf.gnu.narkive.com/gTAVmfKD/how-to-cancel-flags-set-by-ac-lang-werror + # + save_ac_c_werror_flag="$ac_c_werror_flag" + ac_c_werror_flag=yes + # + # We use AC_LANG_SOURCE() so that we can control the complete + # content of the program being compiled. We do not, for example, + # want the default "int main()" that AC_LANG_PROGRAM() generates, + # as it will generate a warning with -Wold-style-definition, meaning + # that we would treat it as not working, as the test will fail if + # *any* error output, including a warning due to the flag we're + # testing, is generated; see + # + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # + # This may, as per those two messages, be fixed in autoconf 2.70, + # but we only require 2.64 or newer for now. + # + AC_COMPILE_IFELSE( + [AC_LANG_SOURCE([[int main(void) { return 0; }]])], [ AC_MSG_RESULT([yes]) can_add_to_cflags=yes @@ -332,6 +339,7 @@ AC_DEFUN(AC_LBL_CHECK_COMPILER_OPT, AC_MSG_RESULT([no]) CFLAGS="$save_CFLAGS" ]) + ac_c_werror_flag="$save_ac_c_werror_flag" ]) dnl @@ -425,14 +433,14 @@ AC_DEFUN(AC_LBL_CHECK_DEPENDENCY_GENERATION_OPT, if AC_RUN_LOG([eval "$CC $ac_lbl_dependency_flag conftest.c >/dev/null 2>&1"]); then AC_MSG_RESULT([yes, with $ac_lbl_dependency_flag]) DEPENDENCY_CFLAG="$ac_lbl_dependency_flag" - MKDEP='${srcdir}/mkdep' + MKDEP='${top_srcdir}/mkdep' else AC_MSG_RESULT([no]) # # We can't run mkdep, so have "make depend" do # nothing. # - MKDEP='${srcdir}/nomkdep' + MKDEP='${top_srcdir}/nomkdep' fi rm -rf conftest* else @@ -441,7 +449,7 @@ AC_DEFUN(AC_LBL_CHECK_DEPENDENCY_GENERATION_OPT, # We can't run mkdep, so have "make depend" do # nothing. # - MKDEP='${srcdir}/nomkdep' + MKDEP='${top_srcdir}/nomkdep' fi AC_SUBST(DEPENDENCY_CFLAG) AC_SUBST(MKDEP) @@ -460,7 +468,6 @@ dnl V_SHLIB_CCOPT (modified to build position-independent code) dnl V_SHLIB_CMD dnl V_SHLIB_OPT dnl V_SONAME_OPT -dnl V_RPATH_OPT dnl AC_DEFUN(AC_LBL_SHLIBS_INIT, [AC_PREREQ(2.50) @@ -484,11 +491,12 @@ AC_DEFUN(AC_LBL_SHLIBS_INIT, aix*) ;; - freebsd*|netbsd*|openbsd*|dragonfly*|linux*|osf*|midipix*) - # - # Platforms where the linker is the GNU linker - # or accepts command-line arguments like - # those the GNU linker accepts. + freebsd*|netbsd*|openbsd*|dragonfly*|linux*|osf*|haiku*|midipix*) + # + # Platforms where the C compiler is GCC or accepts + # compatible command-line arguments, and the linker + # is the GNU linker or accepts compatible command-line + # arguments. # # Some instruction sets require -fPIC on some # operating systems. Check for them. If you @@ -509,12 +517,11 @@ AC_DEFUN(AC_LBL_SHLIBS_INIT, esac V_SHLIB_CCOPT="$V_SHLIB_CCOPT $PIC_OPT" V_SONAME_OPT="-Wl,-soname," - V_RPATH_OPT="-Wl,-rpath," ;; hpux*) V_SHLIB_CCOPT="$V_SHLIB_CCOPT -fpic" - # + # # XXX - this assumes GCC is using the HP linker, # rather than the GNU linker, and that the "+h" # option is used on all HP-UX platforms, both .sl @@ -522,7 +529,7 @@ AC_DEFUN(AC_LBL_SHLIBS_INIT, # V_SONAME_OPT="-Wl,+h," # - # By default, directories specifed with -L + # By default, directories specified with -L # are added to the run-time search path, so # we don't add them in pcap-config. # @@ -531,11 +538,12 @@ AC_DEFUN(AC_LBL_SHLIBS_INIT, solaris*) V_SHLIB_CCOPT="$V_SHLIB_CCOPT -fpic" # - # XXX - this assumes GCC is using the Sun linker, - # rather than the GNU linker. + # Sun/Oracle's C compiler, GCC, and GCC-compatible + # compilers support -Wl,{comma-separated list of options}, + # and we use the C compiler, not ld, for all linking, + # including linking to produce a shared library. # V_SONAME_OPT="-Wl,-h," - V_RPATH_OPT="-Wl,-R," ;; esac else @@ -557,7 +565,7 @@ AC_DEFUN(AC_LBL_SHLIBS_INIT, # "-Wl,-soname,{soname}" option, with the soname part # of the option, while on other platforms the C compiler # driver takes it as a regular option with the soname - # following the option. The same applies to V_RPATH_OPT. + # following the option. # case "$host_os" in @@ -568,13 +576,17 @@ AC_DEFUN(AC_LBL_SHLIBS_INIT, freebsd*|netbsd*|openbsd*|dragonfly*|linux*) # - # "cc" is GCC. + # Platforms where the C compiler is GCC or accepts + # compatible command-line arguments, and the linker + # is the GNU linker or accepts compatible command-line + # arguments. + # + # XXX - does 64-bit SPARC require -fPIC? # V_SHLIB_CCOPT="$V_SHLIB_CCOPT -fpic" V_SHLIB_CMD="\$(CC)" V_SHLIB_OPT="-shared" V_SONAME_OPT="-Wl,-soname," - V_RPATH_OPT="-Wl,-rpath," ;; hpux*) @@ -583,29 +595,33 @@ AC_DEFUN(AC_LBL_SHLIBS_INIT, V_SHLIB_OPT="-b" V_SONAME_OPT="+h " # - # By default, directories specifed with -L + # By default, directories specified with -L # are added to the run-time search path, so # we don't add them in pcap-config. # ;; osf*) - # + # # Presumed to be DEC OSF/1, Digital UNIX, or # Tru64 UNIX. # V_SHLIB_CMD="\$(CC)" V_SHLIB_OPT="-shared" V_SONAME_OPT="-soname " - V_RPATH_OPT="-rpath " ;; solaris*) V_SHLIB_CCOPT="$V_SHLIB_CCOPT -Kpic" V_SHLIB_CMD="\$(CC)" V_SHLIB_OPT="-G" - V_SONAME_OPT="-h " - V_RPATH_OPT="-R" + # + # Sun/Oracle's C compiler, GCC, and GCC-compatible + # compilers support -Wl,{comma-separated list of options}, + # and we use the C compiler, not ld, for all linking, + # including linking to produce a shared library. + # + V_SONAME_OPT="-Wl,-h," ;; esac fi @@ -662,6 +678,46 @@ AC_DEFUN(AC_LBL_C_INLINE, fi AC_DEFINE_UNQUOTED(inline, $ac_cv_lbl_inline, [Define as token for inline if inlining supported])]) +# +# Test whether we have __atomic_load_n() and __atomic_store_n(). +# +# We use AC_TRY_LINK because AC_TRY_COMPILE will succeed, as the +# compiler will just think that those functions are undefined, +# and perhaps warn about that, but not fail to compile. +# +AC_DEFUN(AC_PCAP_C___ATOMICS, + [ + AC_MSG_CHECKING(for __atomic_load_n) + AC_CACHE_VAL(ac_cv_have___atomic_load_n, + AC_TRY_LINK([], + [ + int i = 17; + int j; + j = __atomic_load_n(&i, __ATOMIC_RELAXED); + ], + ac_have___atomic_load_n=yes, + ac_have___atomic_load_n=no)) + AC_MSG_RESULT($ac_have___atomic_load_n) + if test $ac_have___atomic_load_n = yes ; then + AC_DEFINE(HAVE___ATOMIC_LOAD_N, 1, + [define if __atomic_load_n is supported by the compiler]) + fi + + AC_MSG_CHECKING(for __atomic_store_n) + AC_CACHE_VAL(ac_cv_have___atomic_store_n, + AC_TRY_LINK([], + [ + int i; + __atomic_store_n(&i, 17, __ATOMIC_RELAXED); + ], + ac_have___atomic_store_n=yes, + ac_have___atomic_store_n=no)) + AC_MSG_RESULT($ac_have___atomic_store_n) + if test $ac_have___atomic_store_n = yes ; then + AC_DEFINE(HAVE___ATOMIC_STORE_N, 1, + [define if __atomic_store_n is supported by the compiler]) + fi]) + dnl dnl If using gcc, make sure we have ANSI ioctl definitions dnl @@ -752,106 +808,6 @@ AC_DEFUN(AC_LBL_HAVE_RUN_PATH, AC_MSG_RESULT($ac_cv_lbl_have_run_path) ]) -dnl -dnl Checks to see if unaligned memory accesses fail -dnl -dnl usage: -dnl -dnl AC_LBL_UNALIGNED_ACCESS -dnl -dnl results: -dnl -dnl LBL_ALIGN (DEFINED) -dnl -AC_DEFUN(AC_LBL_UNALIGNED_ACCESS, - [AC_MSG_CHECKING(if unaligned accesses fail) - AC_CACHE_VAL(ac_cv_lbl_unaligned_fail, - [case "$host_cpu" in - - # - # These are CPU types where: - # - # the CPU faults on an unaligned access, but at least some - # OSes that support that CPU catch the fault and simulate - # the unaligned access (e.g., Alpha/{Digital,Tru64} UNIX) - - # the simulation is slow, so we don't want to use it; - # - # the CPU, I infer (from the old - # - # XXX: should also check that they don't do weird things (like on arm) - # - # comment) doesn't fault on unaligned accesses, but doesn't - # do a normal unaligned fetch, either (e.g., presumably, ARM); - # - # for whatever reason, the test program doesn't work - # (this has been claimed to be the case for several of those - # CPUs - I don't know what the problem is; the problem - # was reported as "the test program dumps core" for SuperH, - # but that's what the test program is *supposed* to do - - # it dumps core before it writes anything, so the test - # for an empty output file should find an empty output - # file and conclude that unaligned accesses don't work). - # - # This run-time test won't work if you're cross-compiling, so - # in order to support cross-compiling for a particular CPU, - # we have to wire in the list of CPU types anyway, as far as - # I know, so perhaps we should just have a set of CPUs on - # which we know it doesn't work, a set of CPUs on which we - # know it does work, and have the script just fail on other - # cpu types and update it when such a failure occurs. - # - alpha*|arm*|bfin*|hp*|mips*|sh*|sparc*|ia64|nv1) - ac_cv_lbl_unaligned_fail=yes - ;; - - *) - cat >conftest.c < -# include -# include - unsigned char a[[5]] = { 1, 2, 3, 4, 5 }; - main() { - unsigned int i; - pid_t pid; - int status; - /* avoid "core dumped" message */ - pid = fork(); - if (pid < 0) - exit(2); - if (pid > 0) { - /* parent */ - pid = waitpid(pid, &status, 0); - if (pid < 0) - exit(3); - exit(!WIFEXITED(status)); - } - /* child */ - i = *(unsigned int *)&a[[1]]; - printf("%d\n", i); - exit(0); - } -EOF - ${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS \ - conftest.c $LIBS >/dev/null 2>&1 - if test ! -x conftest ; then - dnl failed to compile for some reason - ac_cv_lbl_unaligned_fail=yes - else - ./conftest >conftest.out - if test ! -s conftest.out ; then - ac_cv_lbl_unaligned_fail=yes - else - ac_cv_lbl_unaligned_fail=no - fi - fi - rm -f -r conftest* core core.conftest - ;; - esac]) - AC_MSG_RESULT($ac_cv_lbl_unaligned_fail) - if test $ac_cv_lbl_unaligned_fail = yes ; then - AC_DEFINE(LBL_ALIGN,1,[if unaligned access fails]) - fi]) - dnl dnl If the file .devel exists: dnl Add some warning flags if the compiler supports them @@ -877,16 +833,16 @@ AC_DEFUN(AC_LBL_DEVEL, # Skip all the warning option stuff on some compilers. # if test "$ac_lbl_cc_dont_try_gcc_dashW" != yes; then - AC_LBL_CHECK_UNKNOWN_WARNING_OPTION_ERROR() AC_LBL_CHECK_COMPILER_OPT($1, -W) AC_LBL_CHECK_COMPILER_OPT($1, -Wall) AC_LBL_CHECK_COMPILER_OPT($1, -Wcomma) - AC_LBL_CHECK_COMPILER_OPT($1, -Wdeclaration-after-statement) AC_LBL_CHECK_COMPILER_OPT($1, -Wdocumentation) AC_LBL_CHECK_COMPILER_OPT($1, -Wformat-nonliteral) AC_LBL_CHECK_COMPILER_OPT($1, -Wmissing-noreturn) AC_LBL_CHECK_COMPILER_OPT($1, -Wmissing-prototypes) AC_LBL_CHECK_COMPILER_OPT($1, -Wmissing-variable-declarations) + AC_LBL_CHECK_COMPILER_OPT($1, -Wpointer-arith) + AC_LBL_CHECK_COMPILER_OPT($1, -Wpointer-sign) AC_LBL_CHECK_COMPILER_OPT($1, -Wshadow) AC_LBL_CHECK_COMPILER_OPT($1, -Wsign-compare) AC_LBL_CHECK_COMPILER_OPT($1, -Wstrict-prototypes) @@ -930,6 +886,7 @@ testme(unsigned short a) } ], [generates warnings from ntohs()]) + AC_LBL_CHECK_COMPILER_OPT($1, -Wshorten-64-to-32) fi AC_LBL_CHECK_DEPENDENCY_GENERATION_OPT() # @@ -1057,9 +1014,22 @@ AC_DEFUN(AC_LBL_LIBRARY_NET, [ ], [ # - # We didn't find it. + # Not found in libsocket; test for it in libnetwork, which + # is where it is in Haiku. # - AC_MSG_ERROR([getaddrinfo is required, but wasn't found]) + AC_CHECK_LIB(network, getaddrinfo, + [ + # + # OK, we found it in libnetwork. + # + LIBS="-lnetwork $LIBS" + ], + [ + # + # We didn't find it. + # + AC_MSG_ERROR([getaddrinfo is required, but wasn't found]) + ]) ], -lnsl) # @@ -1077,3 +1047,282 @@ AC_DEFUN(AC_LBL_LIBRARY_NET, [ # DLPI needs putmsg under HPUX so test for -lstr while we're at it AC_SEARCH_LIBS(putmsg, str) ]) + +m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) +dnl pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- +dnl serial 11 (pkg-config-0.29) +dnl +dnl Copyright © 2004 Scott James Remnant . +dnl Copyright © 2012-2015 Dan Nicholson +dnl +dnl This program is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 2 of the License, or +dnl (at your option) any later version. +dnl +dnl This program is distributed in the hope that it will be useful, but +dnl WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +dnl General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this program; if not, write to the Free Software +dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +dnl 02111-1307, USA. +dnl +dnl As a special exception to the GNU General Public License, if you +dnl distribute this file as part of a program that contains a +dnl configuration script generated by Autoconf, you may include it under +dnl the same distribution terms that you use for the rest of that +dnl program. + +dnl PKG_PREREQ(MIN-VERSION) +dnl ----------------------- +dnl Since: 0.29 +dnl +dnl Verify that the version of the pkg-config macros are at least +dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's +dnl installed version of pkg-config, this checks the developer's version +dnl of pkg.m4 when generating configure. +dnl +dnl To ensure that this macro is defined, also add: +dnl m4_ifndef([PKG_PREREQ], +dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])]) +dnl +dnl See the "Since" comment for each macro you use to see what version +dnl of the macros you require. +m4_defun([PKG_PREREQ], +[m4_define([PKG_MACROS_VERSION], [0.29]) +m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, + [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) +])dnl PKG_PREREQ + +dnl PKG_PROG_PKG_CONFIG([MIN-VERSION]) +dnl ---------------------------------- +dnl Since: 0.16 +dnl +dnl Search for the pkg-config tool and set the PKG_CONFIG variable to +dnl first found in the path. Checks that the version of pkg-config found +dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is +dnl used since that's the first version where most current features of +dnl pkg-config existed. +AC_DEFUN([PKG_PROG_PKG_CONFIG], +[m4_pattern_forbid([^_?PKG_[A-Z_]+$]) +m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) +m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) +AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) +AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) +AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) + +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=m4_default([$1], [0.9.0]) + AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + PKG_CONFIG="" + fi +fi[]dnl +])dnl PKG_PROG_PKG_CONFIG + +dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +dnl ------------------------------------------------------------------- +dnl Since: 0.18 +dnl +dnl Check to see whether a particular set of modules exists. Similar to +dnl PKG_CHECK_MODULES(), but does not set variables or print errors. +AC_DEFUN([PKG_CHECK_EXISTS], +[ +if test -n "$PKG_CONFIG" && \ + AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then + m4_default([$2], [:]) +m4_ifvaln([$3], [else + $3])dnl +fi]) + +dnl _PKG_CONFIG([VARIABLE], [FLAGS], [MODULES]) +dnl --------------------------------------------- +dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting +dnl pkg_failed based on the result. +m4_define([_PKG_CONFIG], +[if test -n "$$1"; then + pkg_cv_[]$1="$$1" + elif test -n "$PKG_CONFIG"; then + PKG_CHECK_EXISTS([$3], + [pkg_cv_[]$1=`$PKG_CONFIG $2 "$3" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes ], + [pkg_failed=yes]) + else + pkg_failed=untried +fi[]dnl +])dnl _PKG_CONFIG + +dnl _PKG_SHORT_ERRORS_SUPPORTED +dnl --------------------------- +dnl Internal check to see if pkg-config supports short errors. +AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], +[ +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi[]dnl +])dnl _PKG_SHORT_ERRORS_SUPPORTED + + +dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +dnl [ACTION-IF-NOT-FOUND]) +dnl -------------------------------------------------------------- +dnl Since: 0.4.0 +AC_DEFUN([PKG_CHECK_MODULES], +[ +AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $2, overriding pkg-config])dnl +AC_ARG_VAR([$1][_LIBS], [linker flags for $2, overriding pkg-config])dnl +AC_ARG_VAR([$1][_LIBS_STATIC], [static-link linker flags for $2, overriding pkg-config])dnl + +pkg_failed=no +AC_MSG_CHECKING([for $2 with pkg-config]) +PKG_CHECK_EXISTS($2, + [ + # + # The package was found, so try to get its C flags and + # libraries. + # + _PKG_CONFIG([$1][_CFLAGS], [--cflags], [$2]) + _PKG_CONFIG([$1][_LIBS], [--libs], [$2]) + _PKG_CONFIG([$1][_LIBS_STATIC], [--libs --static], [$2]) + + m4_define([_PKG_TEXT], [ +Alternatively, you may set the environment variables $1[]_CFLAGS +and $1[]_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details.]) + + if test $pkg_failed = yes; then + # + # That failed - report an error. + # + AC_MSG_RESULT([error]) + _PKG_SHORT_ERRORS_SUPPORTED + if test $_pkg_short_errors_supported = yes; then + $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` + else + $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD + + m4_default([$4], [AC_MSG_ERROR( +[Package requirements ($2) were not met: + +$$1_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +_PKG_TEXT])[]dnl + ]) + elif test $pkg_failed = untried; then + # + # We don't have pkg-config, so it didn't work. + # + AC_MSG_RESULT([not found (pkg-config not found)]) + else + # + # We found the package. + # + $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS + $1[]_LIBS=$pkg_cv_[]$1[]_LIBS + $1[]_LIBS_STATIC=$pkg_cv_[]$1[]_LIBS_STATIC + AC_MSG_RESULT([found]) + $3 + fi[]dnl + ], + [ + # + # The package isn't present. + # + AC_MSG_RESULT([not found]) + ]) +])dnl PKG_CHECK_MODULES + + +dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +dnl [ACTION-IF-NOT-FOUND]) +dnl --------------------------------------------------------------------- +dnl Since: 0.29 +dnl +dnl Checks for existence of MODULES and gathers its build flags with +dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags +dnl and VARIABLE-PREFIX_LIBS from --libs. +AC_DEFUN([PKG_CHECK_MODULES_STATIC], +[ +_save_PKG_CONFIG=$PKG_CONFIG +PKG_CONFIG="$PKG_CONFIG --static" +PKG_CHECK_MODULES($@) +PKG_CONFIG=$_save_PKG_CONFIG[]dnl +])dnl PKG_CHECK_MODULES_STATIC + + +dnl PKG_INSTALLDIR([DIRECTORY]) +dnl ------------------------- +dnl Since: 0.27 +dnl +dnl Substitutes the variable pkgconfigdir as the location where a module +dnl should install pkg-config .pc files. By default the directory is +dnl $libdir/pkgconfig, but the default can be changed by passing +dnl DIRECTORY. The user can override through the --with-pkgconfigdir +dnl parameter. +AC_DEFUN([PKG_INSTALLDIR], +[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) +m4_pushdef([pkg_description], + [pkg-config installation directory @<:@]pkg_default[@:>@]) +AC_ARG_WITH([pkgconfigdir], + [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, + [with_pkgconfigdir=]pkg_default) +AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) +m4_popdef([pkg_default]) +m4_popdef([pkg_description]) +])dnl PKG_INSTALLDIR + + +dnl PKG_NOARCH_INSTALLDIR([DIRECTORY]) +dnl -------------------------------- +dnl Since: 0.27 +dnl +dnl Substitutes the variable noarch_pkgconfigdir as the location where a +dnl module should install arch-independent pkg-config .pc files. By +dnl default the directory is $datadir/pkgconfig, but the default can be +dnl changed by passing DIRECTORY. The user can override through the +dnl --with-noarch-pkgconfigdir parameter. +AC_DEFUN([PKG_NOARCH_INSTALLDIR], +[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) +m4_pushdef([pkg_description], + [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) +AC_ARG_WITH([noarch-pkgconfigdir], + [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, + [with_noarch_pkgconfigdir=]pkg_default) +AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) +m4_popdef([pkg_default]) +m4_popdef([pkg_description]) +])dnl PKG_NOARCH_INSTALLDIR + + +dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, +dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +dnl ------------------------------------------- +dnl Since: 0.28 +dnl +dnl Retrieves the value of the pkg-config variable for the given module. +AC_DEFUN([PKG_CHECK_VAR], +[ +AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl + +_PKG_CONFIG([$1], [--variable="][$3]["], [$2]) +AS_VAR_COPY([$1], [pkg_cv_][$1]) + +AS_VAR_IF([$1], [""], [$5], [$4])dnl +])dnl PKG_CHECK_VAR diff --git a/atmuni31.h b/atmuni31.h index 0f85430098ce..7d4f270be9d0 100644 --- a/atmuni31.h +++ b/atmuni31.h @@ -76,7 +76,7 @@ #define PROTO_POS 0 /* offset of protocol discriminator */ #define CALL_REF_POS 2 /* offset of call reference value */ #define MSG_TYPE_POS 5 /* offset of message type */ -#define MSG_LEN_POS 7 /* offset of mesage length */ +#define MSG_LEN_POS 7 /* offset of message length */ #define IE_BEGIN_POS 9 /* offset of first information element */ /* format of signalling messages */ diff --git a/bpf_filter.c b/bpf_filter.c index 33872ff4bbda..8691d0d1a882 100644 --- a/bpf_filter.c +++ b/bpf_filter.c @@ -44,6 +44,11 @@ #include #include "pcap-types.h" +#include "extract.h" +#include "diag-control.h" + +#define EXTRACT_SHORT EXTRACT_BE_U_2 +#define EXTRACT_LONG EXTRACT_BE_U_4 #ifndef _WIN32 #include @@ -55,42 +60,6 @@ #include -#define int32 bpf_int32 -#define u_int32 bpf_u_int32 - -#ifndef LBL_ALIGN -/* - * XXX - IA-64? If not, this probably won't work on Win64 IA-64 - * systems, unless LBL_ALIGN is defined elsewhere for them. - * XXX - SuperH? If not, this probably won't work on WinCE SuperH - * systems, unless LBL_ALIGN is defined elsewhere for them. - */ -#if defined(sparc) || defined(__sparc__) || defined(mips) || \ - defined(ibm032) || defined(__alpha) || defined(__hpux) || \ - defined(__arm__) -#define LBL_ALIGN -#endif -#endif - -#ifndef LBL_ALIGN -#ifndef _WIN32 -#include -#endif - -#define EXTRACT_SHORT(p) ((u_short)ntohs(*(u_short *)p)) -#define EXTRACT_LONG(p) (ntohl(*(u_int32 *)p)) -#else -#define EXTRACT_SHORT(p)\ - ((u_short)\ - ((u_short)*((u_char *)p+0)<<8|\ - (u_short)*((u_char *)p+1)<<0)) -#define EXTRACT_LONG(p)\ - ((u_int32)*((u_char *)p+0)<<24|\ - (u_int32)*((u_char *)p+1)<<16|\ - (u_int32)*((u_char *)p+2)<<8|\ - (u_int32)*((u_char *)p+3)<<0) -#endif - #ifdef __linux__ #include #include @@ -115,13 +84,19 @@ enum { * * Thanks to Ani Sinha for providing initial implementation */ +#if defined(SKF_AD_VLAN_TAG_PRESENT) u_int -bpf_filter_with_aux_data(const struct bpf_insn *pc, const u_char *p, - u_int wirelen, u_int buflen, const struct bpf_aux_data *aux_data) +pcap_filter_with_aux_data(const struct bpf_insn *pc, const u_char *p, + u_int wirelen, u_int buflen, const struct pcap_bpf_aux_data *aux_data) +#else +u_int +pcap_filter_with_aux_data(const struct bpf_insn *pc, const u_char *p, + u_int wirelen, u_int buflen, const struct pcap_bpf_aux_data *aux_data _U_) +#endif { - register u_int32 A, X; + register uint32_t A, X; register bpf_u_int32 k; - u_int32 mem[BPF_MEMWORDS]; + uint32_t mem[BPF_MEMWORDS]; if (pc == 0) /* @@ -160,6 +135,13 @@ bpf_filter_with_aux_data(const struct bpf_insn *pc, const u_char *p, continue; case BPF_LD|BPF_B|BPF_ABS: + /* + * Yes, we know, this switch doesn't do + * anything unless we're building for + * a Linux kernel with removed VLAN + * tags available as meta-data. + */ +DIAG_OFF_DEFAULT_ONLY_SWITCH switch (pc->k) { #if defined(SKF_AD_VLAN_TAG_PRESENT) @@ -183,6 +165,7 @@ bpf_filter_with_aux_data(const struct bpf_insn *pc, const u_char *p, A = p[k]; break; } +DIAG_ON_DEFAULT_ONLY_SWITCH continue; case BPF_LD|BPF_W|BPF_LEN: @@ -405,13 +388,12 @@ bpf_filter_with_aux_data(const struct bpf_insn *pc, const u_char *p, } u_int -bpf_filter(const struct bpf_insn *pc, const u_char *p, u_int wirelen, +pcap_filter(const struct bpf_insn *pc, const u_char *p, u_int wirelen, u_int buflen) { - return bpf_filter_with_aux_data(pc, p, wirelen, buflen, NULL); + return pcap_filter_with_aux_data(pc, p, wirelen, buflen, NULL); } - /* * Return true if the 'fcode' is a valid filter program. * The constraints are that each jump be forward and to a valid @@ -424,7 +406,7 @@ bpf_filter(const struct bpf_insn *pc, const u_char *p, u_int wirelen, * Otherwise, a bogus program could easily crash the system. */ int -bpf_validate(const struct bpf_insn *f, int len) +pcap_validate_filter(const struct bpf_insn *f, int len) { u_int i, from; const struct bpf_insn *p; @@ -546,3 +528,19 @@ bpf_validate(const struct bpf_insn *f, int len) } return BPF_CLASS(f[len - 1].code) == BPF_RET; } + +/* + * Exported because older versions of libpcap exported them. + */ +u_int +bpf_filter(const struct bpf_insn *pc, const u_char *p, u_int wirelen, + u_int buflen) +{ + return pcap_filter(pc, p, wirelen, buflen); +} + +int +bpf_validate(const struct bpf_insn *f, int len) +{ + return pcap_validate_filter(f, len); +} diff --git a/bpf_image.c b/bpf_image.c index ab41d1ef98bd..e48c76d54ed8 100644 --- a/bpf_image.c +++ b/bpf_image.c @@ -28,12 +28,104 @@ #include #include +#ifdef __linux__ +#include +#include +#include + +/* + * We want our versions of these #defines, not Linux's version. + * (The two should be the same; if not, we have a problem; all BPF + * implementations *should* be source-compatible supersets of ours.) + */ +#undef BPF_STMT +#undef BPF_JUMP +#endif + #include "pcap-int.h" #ifdef HAVE_OS_PROTO_H #include "os-proto.h" #endif +#ifdef SKF_AD_OFF +/* + * Symbolic names for offsets that refer to the special Linux BPF locations. + */ +static const char *offsets[SKF_AD_MAX] = { +#ifdef SKF_AD_PROTOCOL + [SKF_AD_PROTOCOL] = "proto", +#endif +#ifdef SKF_AD_PKTTYPE + [SKF_AD_PKTTYPE] = "type", +#endif +#ifdef SKF_AD_IFINDEX + [SKF_AD_IFINDEX] = "ifidx", +#endif +#ifdef SKF_AD_NLATTR + [SKF_AD_NLATTR] = "nla", +#endif +#ifdef SKF_AD_NLATTR_NEST + [SKF_AD_NLATTR_NEST] = "nlan", +#endif +#ifdef SKF_AD_MARK + [SKF_AD_MARK] = "mark", +#endif +#ifdef SKF_AD_QUEUE + [SKF_AD_QUEUE] = "queue", +#endif +#ifdef SKF_AD_HATYPE + [SKF_AD_HATYPE] = "hatype", +#endif +#ifdef SKF_AD_RXHASH + [SKF_AD_RXHASH] = "rxhash", +#endif +#ifdef SKF_AD_CPU + [SKF_AD_CPU] = "cpu", +#endif +#ifdef SKF_AD_ALU_XOR_X + [SKF_AD_ALU_XOR_X] = "xor_x", +#endif +#ifdef SKF_AD_VLAN_TAG + [SKF_AD_VLAN_TAG] = "vlan_tci", +#endif +#ifdef SKF_AD_VLAN_TAG_PRESENT + [SKF_AD_VLAN_TAG_PRESENT] = "vlanp", +#endif +#ifdef SKF_AD_PAY_OFFSET + [SKF_AD_PAY_OFFSET] = "poff", +#endif +#ifdef SKF_AD_RANDOM + [SKF_AD_RANDOM] = "random", +#endif +#ifdef SKF_AD_VLAN_TPID + [SKF_AD_VLAN_TPID] = "vlan_tpid" +#endif +}; +#endif + +static void +bpf_print_abs_load_operand(char *buf, size_t bufsize, const struct bpf_insn *p) +{ +#ifdef SKF_AD_OFF + const char *sym; + + /* + * It's an absolute load. + * Is the offset a special Linux offset that we know about? + */ + if (p->k >= (bpf_u_int32)SKF_AD_OFF && + p->k < (bpf_u_int32)(SKF_AD_OFF + SKF_AD_MAX) && + (sym = offsets[p->k - (bpf_u_int32)SKF_AD_OFF]) != NULL) { + /* + * Yes. Print the offset symbolically. + */ + (void)snprintf(buf, bufsize, "[%s]", sym); + } else +#endif + (void)snprintf(buf, bufsize, "[%d]", p->k); +} + char * bpf_image(const struct bpf_insn *p, int n) { @@ -46,13 +138,13 @@ bpf_image(const struct bpf_insn *p, int n) default: op = "unimp"; - (void)pcap_snprintf(operand_buf, sizeof operand_buf, "0x%x", p->code); + (void)snprintf(operand_buf, sizeof operand_buf, "0x%x", p->code); operand = operand_buf; break; case BPF_RET|BPF_K: op = "ret"; - (void)pcap_snprintf(operand_buf, sizeof operand_buf, "#%d", p->k); + (void)snprintf(operand_buf, sizeof operand_buf, "#%d", p->k); operand = operand_buf; break; @@ -63,19 +155,19 @@ bpf_image(const struct bpf_insn *p, int n) case BPF_LD|BPF_W|BPF_ABS: op = "ld"; - (void)pcap_snprintf(operand_buf, sizeof operand_buf, "[%d]", p->k); + bpf_print_abs_load_operand(operand_buf, sizeof operand_buf, p); operand = operand_buf; break; case BPF_LD|BPF_H|BPF_ABS: op = "ldh"; - (void)pcap_snprintf(operand_buf, sizeof operand_buf, "[%d]", p->k); + bpf_print_abs_load_operand(operand_buf, sizeof operand_buf, p); operand = operand_buf; break; case BPF_LD|BPF_B|BPF_ABS: op = "ldb"; - (void)pcap_snprintf(operand_buf, sizeof operand_buf, "[%d]", p->k); + bpf_print_abs_load_operand(operand_buf, sizeof operand_buf, p); operand = operand_buf; break; @@ -86,91 +178,91 @@ bpf_image(const struct bpf_insn *p, int n) case BPF_LD|BPF_W|BPF_IND: op = "ld"; - (void)pcap_snprintf(operand_buf, sizeof operand_buf, "[x + %d]", p->k); + (void)snprintf(operand_buf, sizeof operand_buf, "[x + %d]", p->k); operand = operand_buf; break; case BPF_LD|BPF_H|BPF_IND: op = "ldh"; - (void)pcap_snprintf(operand_buf, sizeof operand_buf, "[x + %d]", p->k); + (void)snprintf(operand_buf, sizeof operand_buf, "[x + %d]", p->k); operand = operand_buf; break; case BPF_LD|BPF_B|BPF_IND: op = "ldb"; - (void)pcap_snprintf(operand_buf, sizeof operand_buf, "[x + %d]", p->k); + (void)snprintf(operand_buf, sizeof operand_buf, "[x + %d]", p->k); operand = operand_buf; break; case BPF_LD|BPF_IMM: op = "ld"; - (void)pcap_snprintf(operand_buf, sizeof operand_buf, "#0x%x", p->k); + (void)snprintf(operand_buf, sizeof operand_buf, "#0x%x", p->k); operand = operand_buf; break; case BPF_LDX|BPF_IMM: op = "ldx"; - (void)pcap_snprintf(operand_buf, sizeof operand_buf, "#0x%x", p->k); + (void)snprintf(operand_buf, sizeof operand_buf, "#0x%x", p->k); operand = operand_buf; break; case BPF_LDX|BPF_MSH|BPF_B: op = "ldxb"; - (void)pcap_snprintf(operand_buf, sizeof operand_buf, "4*([%d]&0xf)", p->k); + (void)snprintf(operand_buf, sizeof operand_buf, "4*([%d]&0xf)", p->k); operand = operand_buf; break; case BPF_LD|BPF_MEM: op = "ld"; - (void)pcap_snprintf(operand_buf, sizeof operand_buf, "M[%d]", p->k); + (void)snprintf(operand_buf, sizeof operand_buf, "M[%d]", p->k); operand = operand_buf; break; case BPF_LDX|BPF_MEM: op = "ldx"; - (void)pcap_snprintf(operand_buf, sizeof operand_buf, "M[%d]", p->k); + (void)snprintf(operand_buf, sizeof operand_buf, "M[%d]", p->k); operand = operand_buf; break; case BPF_ST: op = "st"; - (void)pcap_snprintf(operand_buf, sizeof operand_buf, "M[%d]", p->k); + (void)snprintf(operand_buf, sizeof operand_buf, "M[%d]", p->k); operand = operand_buf; break; case BPF_STX: op = "stx"; - (void)pcap_snprintf(operand_buf, sizeof operand_buf, "M[%d]", p->k); + (void)snprintf(operand_buf, sizeof operand_buf, "M[%d]", p->k); operand = operand_buf; break; case BPF_JMP|BPF_JA: op = "ja"; - (void)pcap_snprintf(operand_buf, sizeof operand_buf, "%d", n + 1 + p->k); + (void)snprintf(operand_buf, sizeof operand_buf, "%d", n + 1 + p->k); operand = operand_buf; break; case BPF_JMP|BPF_JGT|BPF_K: op = "jgt"; - (void)pcap_snprintf(operand_buf, sizeof operand_buf, "#0x%x", p->k); + (void)snprintf(operand_buf, sizeof operand_buf, "#0x%x", p->k); operand = operand_buf; break; case BPF_JMP|BPF_JGE|BPF_K: op = "jge"; - (void)pcap_snprintf(operand_buf, sizeof operand_buf, "#0x%x", p->k); + (void)snprintf(operand_buf, sizeof operand_buf, "#0x%x", p->k); operand = operand_buf; break; case BPF_JMP|BPF_JEQ|BPF_K: op = "jeq"; - (void)pcap_snprintf(operand_buf, sizeof operand_buf, "#0x%x", p->k); + (void)snprintf(operand_buf, sizeof operand_buf, "#0x%x", p->k); operand = operand_buf; break; case BPF_JMP|BPF_JSET|BPF_K: op = "jset"; - (void)pcap_snprintf(operand_buf, sizeof operand_buf, "#0x%x", p->k); + (void)snprintf(operand_buf, sizeof operand_buf, "#0x%x", p->k); operand = operand_buf; break; @@ -246,61 +338,61 @@ bpf_image(const struct bpf_insn *p, int n) case BPF_ALU|BPF_ADD|BPF_K: op = "add"; - (void)pcap_snprintf(operand_buf, sizeof operand_buf, "#%d", p->k); + (void)snprintf(operand_buf, sizeof operand_buf, "#%d", p->k); operand = operand_buf; break; case BPF_ALU|BPF_SUB|BPF_K: op = "sub"; - (void)pcap_snprintf(operand_buf, sizeof operand_buf, "#%d", p->k); + (void)snprintf(operand_buf, sizeof operand_buf, "#%d", p->k); operand = operand_buf; break; case BPF_ALU|BPF_MUL|BPF_K: op = "mul"; - (void)pcap_snprintf(operand_buf, sizeof operand_buf, "#%d", p->k); + (void)snprintf(operand_buf, sizeof operand_buf, "#%d", p->k); operand = operand_buf; break; case BPF_ALU|BPF_DIV|BPF_K: op = "div"; - (void)pcap_snprintf(operand_buf, sizeof operand_buf, "#%d", p->k); + (void)snprintf(operand_buf, sizeof operand_buf, "#%d", p->k); operand = operand_buf; break; case BPF_ALU|BPF_MOD|BPF_K: op = "mod"; - (void)pcap_snprintf(operand_buf, sizeof operand_buf, "#%d", p->k); + (void)snprintf(operand_buf, sizeof operand_buf, "#%d", p->k); operand = operand_buf; break; case BPF_ALU|BPF_AND|BPF_K: op = "and"; - (void)pcap_snprintf(operand_buf, sizeof operand_buf, "#0x%x", p->k); + (void)snprintf(operand_buf, sizeof operand_buf, "#0x%x", p->k); operand = operand_buf; break; case BPF_ALU|BPF_OR|BPF_K: op = "or"; - (void)pcap_snprintf(operand_buf, sizeof operand_buf, "#0x%x", p->k); + (void)snprintf(operand_buf, sizeof operand_buf, "#0x%x", p->k); operand = operand_buf; break; case BPF_ALU|BPF_XOR|BPF_K: op = "xor"; - (void)pcap_snprintf(operand_buf, sizeof operand_buf, "#0x%x", p->k); + (void)snprintf(operand_buf, sizeof operand_buf, "#0x%x", p->k); operand = operand_buf; break; case BPF_ALU|BPF_LSH|BPF_K: op = "lsh"; - (void)pcap_snprintf(operand_buf, sizeof operand_buf, "#%d", p->k); + (void)snprintf(operand_buf, sizeof operand_buf, "#%d", p->k); operand = operand_buf; break; case BPF_ALU|BPF_RSH|BPF_K: op = "rsh"; - (void)pcap_snprintf(operand_buf, sizeof operand_buf, "#%d", p->k); + (void)snprintf(operand_buf, sizeof operand_buf, "#%d", p->k); operand = operand_buf; break; @@ -320,11 +412,11 @@ bpf_image(const struct bpf_insn *p, int n) break; } if (BPF_CLASS(p->code) == BPF_JMP && BPF_OP(p->code) != BPF_JA) { - (void)pcap_snprintf(image, sizeof image, + (void)snprintf(image, sizeof image, "(%03d) %-8s %-16s jt %d\tjf %d", n, op, operand, n + 1 + p->jt, n + 1 + p->jf); } else { - (void)pcap_snprintf(image, sizeof image, + (void)snprintf(image, sizeof image, "(%03d) %-8s %s", n, op, operand); } diff --git a/charconv.c b/charconv.c new file mode 100644 index 000000000000..5f97509ab4e3 --- /dev/null +++ b/charconv.c @@ -0,0 +1,217 @@ +/* -*- Mode: c; tab-width: 8; indent-tabs-mode: 1; c-basic-offset: 8; -*- */ +/* + * Copyright (c) 1993, 1994, 1995, 1996, 1997 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Computer Systems + * Engineering Group at Lawrence Berkeley Laboratory. + * 4. Neither the name of the University nor of the Laboratory may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef _WIN32 +#include +#include + +#include /* Needed for PCAP_ERRBUF_SIZE */ + +#include "charconv.h" + +wchar_t * +cp_to_utf_16le(UINT codepage, const char *cp_string, DWORD flags) +{ + int utf16le_len; + wchar_t *utf16le_string; + + /* + * Map from the specified code page to UTF-16LE. + * First, find out how big a buffer we'll need. + */ + utf16le_len = MultiByteToWideChar(codepage, flags, cp_string, -1, + NULL, 0); + if (utf16le_len == 0) { + /* + * Error. Fail with EINVAL. + */ + errno = EINVAL; + return (NULL); + } + + /* + * Now attempt to allocate a buffer for that. + */ + utf16le_string = malloc(utf16le_len * sizeof (wchar_t)); + if (utf16le_string == NULL) { + /* + * Not enough memory; assume errno has been + * set, and fail. + */ + return (NULL); + } + + /* + * Now convert. + */ + utf16le_len = MultiByteToWideChar(codepage, flags, cp_string, -1, + utf16le_string, utf16le_len); + if (utf16le_len == 0) { + /* + * Error. Fail with EINVAL. + * XXX - should this ever happen, given that + * we already ran the string through + * MultiByteToWideChar() to find out how big + * a buffer we needed? + */ + free(utf16le_string); + errno = EINVAL; + return (NULL); + } + return (utf16le_string); +} + +char * +utf_16le_to_cp(UINT codepage, const wchar_t *utf16le_string) +{ + int cp_len; + char *cp_string; + + /* + * Map from UTF-16LE to the specified code page. + * First, find out how big a buffer we'll need. + * We convert composite characters to precomposed characters, + * as that's what Windows expects. + */ + cp_len = WideCharToMultiByte(codepage, WC_COMPOSITECHECK, + utf16le_string, -1, NULL, 0, NULL, NULL); + if (cp_len == 0) { + /* + * Error. Fail with EINVAL. + */ + errno = EINVAL; + return (NULL); + } + + /* + * Now attempt to allocate a buffer for that. + */ + cp_string = malloc(cp_len * sizeof (char)); + if (cp_string == NULL) { + /* + * Not enough memory; assume errno has been + * set, and fail. + */ + return (NULL); + } + + /* + * Now convert. + */ + cp_len = WideCharToMultiByte(codepage, WC_COMPOSITECHECK, + utf16le_string, -1, cp_string, cp_len, NULL, NULL); + if (cp_len == 0) { + /* + * Error. Fail with EINVAL. + * XXX - should this ever happen, given that + * we already ran the string through + * WideCharToMultiByte() to find out how big + * a buffer we needed? + */ + free(cp_string); + errno = EINVAL; + return (NULL); + } + return (cp_string); +} + +/* + * Convert an error message string from UTF-8 to the local code page, as + * best we can. + * + * The buffer is assumed to be PCAP_ERRBUF_SIZE bytes long; we truncate + * if it doesn't fit. + */ +void +utf_8_to_acp_truncated(char *errbuf) +{ + wchar_t *utf_16_errbuf; + int retval; + DWORD err; + + /* + * Do this by converting to UTF-16LE and then to the local + * code page. That means we get to use Microsoft's + * conversion routines, rather than having to understand + * all the code pages ourselves, *and* that this routine + * can convert in place. + */ + + /* + * Map from UTF-8 to UTF-16LE. + * First, find out how big a buffer we'll need. + * Convert any invalid characters to REPLACEMENT CHARACTER. + */ + utf_16_errbuf = cp_to_utf_16le(CP_UTF8, errbuf, 0); + if (utf_16_errbuf == NULL) { + /* + * Error. Give up. + */ + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "Can't convert error string to the local code page"); + return; + } + + /* + * Now, convert that to the local code page. + * Use the current thread's code page. For unconvertable + * characters, let it pick the "best fit" character. + * + * XXX - we'd like some way to do what utf_16le_to_utf_8_truncated() + * does if the buffer isn't big enough, but we don't want to have + * to handle all local code pages ourselves; doing so requires + * knowledge of all those code pages, including knowledge of how + * characters are formed in thoe code pages so that we can avoid + * cutting a multi-byte character into pieces. + * + * Converting to an un-truncated string using Windows APIs, and + * then copying to the buffer, still requires knowledge of how + * characters are formed in the target code page. + */ + retval = WideCharToMultiByte(CP_THREAD_ACP, 0, utf_16_errbuf, -1, + errbuf, PCAP_ERRBUF_SIZE, NULL, NULL); + if (retval == 0) { + err = GetLastError(); + free(utf_16_errbuf); + if (err == ERROR_INSUFFICIENT_BUFFER) + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "The error string, in the local code page, didn't fit in the buffer"); + else + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "Can't convert error string to the local code page"); + return; + } + free(utf_16_errbuf); +} +#endif diff --git a/charconv.h b/charconv.h new file mode 100644 index 000000000000..93103d461ef7 --- /dev/null +++ b/charconv.h @@ -0,0 +1,44 @@ +/* -*- Mode: c; tab-width: 8; indent-tabs-mode: 1; c-basic-offset: 8; -*- */ +/* + * Copyright (c) 1993, 1994, 1995, 1996, 1997 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Computer Systems + * Engineering Group at Lawrence Berkeley Laboratory. + * 4. Neither the name of the University nor of the Laboratory may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef charconv_h +#define charconv_h + +#ifdef _WIN32 +extern wchar_t *cp_to_utf_16le(UINT codepage, const char *cp_string, DWORD flags); +extern char *utf_16le_to_cp(UINT codepage, const wchar_t *utf16le_string); +extern void utf_8_to_acp_truncated(char *); +#endif + +#endif /* charconv_h */ diff --git a/cmake/Modules/FindAirPcap.cmake b/cmake/Modules/FindAirPcap.cmake new file mode 100644 index 000000000000..56c71b7bf10d --- /dev/null +++ b/cmake/Modules/FindAirPcap.cmake @@ -0,0 +1,69 @@ +# +# FindAirPcap +# ========== +# +# Find the AirPcap library and include files. +# +# This module defines the following variables: +# +# AirPcap_INCLUDE_DIR - absolute path to the directory containing airpcap.h. +# +# AirPcap_LIBRARY - relative or absolute path to the AirPcap library to +# link with. An absolute path is will be used if the +# AirPcap library is not located in the compiler's +# default search path. + +# AirPcap_FOUND - TRUE if the AirPcap library *and* header are found. +# +# Hints and Backward Compatibility +# ================================ +# +# To tell this module where to look, a user may set the environment variable +# AirPcap_ROOT to point cmake to the *root* of a directory with include and +# lib subdirectories for airpcap.dll (e.g Airpcap_Devpack). +# Alternatively, AirPcap_ROOT may also be set from the CMake command +# line or GUI (e.g cmake -DAirPcap_ROOT=C:\path\to\airpcap_sdk [...]) +# + +# The 64-bit airpcap.lib is located under /x64 +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + # + # For the WinPcap and Npcap SDKs, the Lib subdirectory of the top-level + # directory contains 32-bit libraries; the 64-bit libraries are in the + # Lib/x64 directory. + # + # The only way to *FORCE* CMake to look in the Lib/x64 directory + # without searching in the Lib directory first appears to be to set + # CMAKE_LIBRARY_ARCHITECTURE to "x64". + # + # In newer versions of CMake, CMAKE_LIBRARY_ARCHITECTURE is set according to + # the language, e.g., CMAKE__LIBRARY_ARCHITECTURE. So, set the new + # variable, CMAKE_C_LIBRARY_ARCHITECTURE, so that CMAKE_LIBRARY_ARCHITECTURE + # inherits the correct value. + # + set(CMAKE_C_LIBRARY_ARCHITECTURE "x64") + set(CMAKE_LIBRARY_ARCHITECTURE "x64") +endif() + +# Find the header +find_path(AirPcap_INCLUDE_DIR airpcap.h + PATH_SUFFIXES include +) + +# Find the library +find_library(AirPcap_LIBRARY + NAMES airpcap +) + +# Set AirPcap_FOUND to TRUE if AirPcap_INCLUDE_DIR and AirPcap_LIBRARY are TRUE. +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(AirPcap + DEFAULT_MSG + AirPcap_INCLUDE_DIR + AirPcap_LIBRARY +) + +mark_as_advanced(AirPcap_INCLUDE_DIR AirPcap_LIBRARY) + +set(AirPcap_INCLUDE_DIRS ${AirPcap_INCLUDE_DIR}) +set(AirPcap_LIBRARIES ${AirPcap_LIBRARY}) diff --git a/cmake/Modules/FindDAG.cmake b/cmake/Modules/FindDAG.cmake index ef1352844558..f41b90a24963 100644 --- a/cmake/Modules/FindDAG.cmake +++ b/cmake/Modules/FindDAG.cmake @@ -14,6 +14,12 @@ find_path(DAG_INCLUDE_DIR dagapi.h) find_library(DAG_LIBRARY dag) find_library(DAGCONF_LIBRARY dagconf) +# +# Get link information from the _LIBRARY paths. +# +get_link_info_from_library_path(DAG dag) +get_link_info_from_library_path(DAGCONF dagconf) + include(FindPackageHandleStandardArgs) find_package_handle_standard_args(DAG DEFAULT_MSG @@ -30,3 +36,4 @@ mark_as_advanced( set(DAG_INCLUDE_DIRS ${DAG_INCLUDE_DIR}) set(DAG_LIBRARIES ${DAG_LIBRARY} ${DAGCONF_LIBRARY}) +set(DAG_STATIC_LIBRARIES ${DAG_LIBRARY} ${DAGCONF_LIBRARY}) diff --git a/cmake/Modules/FindPacket.cmake b/cmake/Modules/FindPacket.cmake index f114875bf873..8224cd3f3e5c 100644 --- a/cmake/Modules/FindPacket.cmake +++ b/cmake/Modules/FindPacket.cmake @@ -28,24 +28,23 @@ # # This module defines the following variables: # -# PACKET_INCLUDE_DIR - absolute path to the directory containing Packet32.h. +# Packet_INCLUDE_DIR - absolute path to the directory containing Packet32.h. # -# PACKET_LIBRARY - relative or absolute path to the Packet library to +# Packet_LIBRARY - relative or absolute path to the Packet library to # link with. An absolute path is will be used if the # Packet library is not located in the compiler's -# default search path. See e.g. PACKET_DLL_DIR -# variable below. +# default search path. -# PACKET_FOUND - TRUE if the Packet library *and* header are found. +# Packet_FOUND - TRUE if the Packet library *and* header are found. # # Hints and Backward Compatibility # ================================ # # To tell this module where to look, a user may set the environment variable -# PACKET_DLL_DIR to point cmake to the *root* of a directory with include and -# lib subdirectories for packet.dll (e.g WpdPack/npcap-sdk). -# Alternatively, PACKET_DLL_DIR may also be set from cmake command line or GUI -# (e.g cmake -DPACKET_DLL_DIR=/path/to/packet [...]) +# Packet_ROOT to point cmake to the *root* of a directory with include and +# lib subdirectories for packet.dll (e.g WpdPack or npcap-sdk). +# Alternatively, Packet_ROOT may also be set from cmake command line or GUI +# (e.g cmake -DPacket_ROOT=C:\path\to\packet [...]) # # The 64-bit Packet.lib is located under /x64 @@ -59,30 +58,52 @@ if(CMAKE_SIZEOF_VOID_P EQUAL 8) # without searching in the Lib directory first appears to be to set # CMAKE_LIBRARY_ARCHITECTURE to "x64". # - set(CMAKE_LIBRARY_ARCHITECTURE "x64") + # In newer versions of CMake, CMAKE_LIBRARY_ARCHITECTURE is set according to + # the language, e.g., CMAKE__LIBRARY_ARCHITECTURE. So, set the new + # variable, CMAKE_C_LIBRARY_ARCHITECTURE, so that CMAKE_LIBRARY_ARCHITECTURE + # inherits the correct value. + # + set(archdetect_c_code " + #ifndef _M_ARM64 + #error Not ARM64 + #endif + int main() { return 0; } + ") + + file(WRITE "${CMAKE_BINARY_DIR}/archdetect.c" "${archdetect_c_code}") + try_compile( + IsArm64 + "${CMAKE_BINARY_DIR}/archdetect" + "${CMAKE_BINARY_DIR}/archdetect.c" + ) + if(IsArm64) + set(CMAKE_C_LIBRARY_ARCHITECTURE "ARM64") + set(CMAKE_LIBRARY_ARCHITECTURE "ARM64") + else() + set(CMAKE_C_LIBRARY_ARCHITECTURE "x64") + set(CMAKE_LIBRARY_ARCHITECTURE "x64") + endif() endif() # Find the header -find_path(PACKET_INCLUDE_DIR Packet32.h - HINTS "${PACKET_DLL_DIR}" ENV PACKET_DLL_DIR +find_path(Packet_INCLUDE_DIR Packet32.h PATH_SUFFIXES include Include ) # Find the library -find_library(PACKET_LIBRARY +find_library(Packet_LIBRARY NAMES Packet packet - HINTS "${PACKET_DLL_DIR}" ENV PACKET_DLL_DIR ) -# Set PACKET_FOUND to TRUE if PACKET_INCLUDE_DIR and PACKET_LIBRARY are TRUE. +# Set Packet_FOUND to TRUE if Packet_INCLUDE_DIR and Packet_LIBRARY are TRUE. include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(PACKET +find_package_handle_standard_args(Packet DEFAULT_MSG - PACKET_INCLUDE_DIR - PACKET_LIBRARY + Packet_INCLUDE_DIR + Packet_LIBRARY ) -mark_as_advanced(PACKET_INCLUDE_DIR PACKET_LIBRARY) +mark_as_advanced(Packet_INCLUDE_DIR Packet_LIBRARY) -set(PACKET_INCLUDE_DIRS ${PACKET_INCLUDE_DIR}) -set(PACKET_LIBRARIES ${PACKET_LIBRARY}) +set(Packet_INCLUDE_DIRS ${Packet_INCLUDE_DIR}) +set(Packet_LIBRARIES ${Packet_LIBRARY}) diff --git a/cmake/Modules/FindSNF.cmake b/cmake/Modules/FindSNF.cmake index 76dcced417fe..d873b5aa74f8 100644 --- a/cmake/Modules/FindSNF.cmake +++ b/cmake/Modules/FindSNF.cmake @@ -8,6 +8,11 @@ find_path(SNF_INCLUDE_DIR snf.h /opt/snf) # Try to find the library find_library(SNF_LIBRARY snf /opt/snf) +# +# Get link information from the _LIBRARY paths. +# +get_link_info_from_library_path(SNF snf) + include(FindPackageHandleStandardArgs) find_package_handle_standard_args(SNF DEFAULT_MSG @@ -22,3 +27,4 @@ mark_as_advanced( set(SNF_INCLUDE_DIRS ${SNF_INCLUDE_DIR}) set(SNF_LIBRARIES ${SNF_LIBRARY}) +set(SNF_STATIC_LIBRARIES ${SNF_LIBRARY}) diff --git a/cmake/Modules/Finddpdk.cmake b/cmake/Modules/Finddpdk.cmake new file mode 100644 index 000000000000..323262afa642 --- /dev/null +++ b/cmake/Modules/Finddpdk.cmake @@ -0,0 +1,118 @@ +# Try to find dpdk +# +# Once done, this will define +# +# dpdk_FOUND +# dpdk_INCLUDE_DIRS +# dpdk_LIBRARIES +# dpdk_STATIC_LIBRARIES +# dpdk_LIBS_STATIC +# dpdk_REQUIRES_PRIVATE +# dpdk_PACKAGE_NAME + +# +# We only try to find DPDK using pkg-config; DPDK is *SO* +# complicated - DPDK 19.02, for example, has about 117(!) +# libraries, and the precise set of libraries required has +# changed over time - so attempting to guess which libraries +# you need, and hardcoding that in an attempt to find the +# libraries without DPDK, rather than relying on DPDK to +# tell you, with a .pc file, what libraries are needed, +# is *EXTREMELY* fragile and has caused some bug reports, +# so we're just not going to do it. +# +# If that causes a problem, the only thing we will do is +# accept an alternative way of finding the appropriate +# library set for the installed version of DPDK that is +# as robust as pkg-config (i.e., it had better work as well +# as pkg-config with *ALL* versions of DPDK that provide a +# libdpdk.pc file). +# +# If dpdk_ROOT is set, add ${dpdk_ROOT}/pkgconfig +# to PKG_CONFIG_PATH, so we look for the .pc file there, +# first. +# +if(PKG_CONFIG_FOUND) + set(save_PKG_CONFIG_PATH $ENV{PKG_CONFIG_PATH}) + if(dpdk_ROOT) + set(ENV{PKG_CONFIG_PATH} "${dpdk_ROOT}/pkgconfig:$ENV{PKG_CONFIG_PATH}") + endif() + pkg_check_modules(dpdk QUIET libdpdk) + if(dpdk_FOUND) + # + # Get link information for DPDK. + # + pkg_get_link_info(dpdk libdpdk) + endif() + set(ENV{PKG_CONFIG_PATH} "${save_PKG_CONFIG_PATH}") +endif() + +mark_as_advanced(dpdk_INCLUDE_DIRS dpdk_LIBRARIES dpdk_STATIC_LIBRARIES dpdk_REQUIRES_PRIVATE) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(dpdk DEFAULT_MSG + dpdk_INCLUDE_DIRS + dpdk_LIBRARIES) + +if(dpdk_FOUND) + # + # This depends on CMake support for "imported targets", + # which are not supported until CMake 3.19. + # + # Ubuntu 20.04 provides CMake 3.16.3, so we are *NOT* + # going to require CMake 3.19. If you want to use + # Shiny New Features(TM), wait until all the OSes on + # which a build might conceivably be done, and that + # provide CMake, provide 3.19 or later. + # + # Just don't do this stuff on earlier versions. If that + # breaks something, figure out a way to do it *without* + # "imported targets", and either do this that way, or, + # at least, do it that way on older versions of CMake. + # + # (One good thing about autotools is that only the builders + # of a package, and people doing configure-script development, + # have to care about the autoconf etc. version; you don't + # even need to have autotools installed in order to be able + # to run an autotools-generated configure script, you just + # need an environment UN*Xy enough, and modern enough, to + # run the stuff in the script. + # + # This is *NOT* the case for CMake; not only do you need + # CMake in order to build a package using CMake, you need + # a version recent enough to run the stuff the package's + # CMake files use. + # + # Please keep this in mind when changing any CMake files, + # and keep in mind what versions of CMake come with, for + # example, commonly-used versions of commonly-used + # Linux distributiions.) + # + if(NOT CMAKE_VERSION VERSION_LESS 3.19) + if(NOT TARGET dpdk::cflags) + if(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64|x86_64|AMD64") + set(rte_cflags "-march=core2") + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm|ARM") + set(rte_cflags "-march=armv7-a") + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|AARCH64") + set(rte_cflags "-march=armv8-a+crc") + endif() + add_library(dpdk::cflags INTERFACE IMPORTED) + if (rte_cflags) + set_target_properties(dpdk::cflags PROPERTIES + INTERFACE_COMPILE_OPTIONS "${rte_cflags}") + endif() + endif() + + if(NOT TARGET dpdk::dpdk) + add_library(dpdk::dpdk INTERFACE IMPORTED) + find_package(Threads QUIET) + list(APPEND dpdk_LIBRARIES + Threads::Threads + dpdk::cflags) + set_target_properties(dpdk::dpdk PROPERTIES + INTERFACE_LINK_LIBRARIES "${dpdk_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${dpdk_INCLUDE_DIRS}") + endif() + endif() +endif() diff --git a/cmakeconfig.h.in b/cmakeconfig.h.in index 1639925e35d6..4ac85cc5303a 100644 --- a/cmakeconfig.h.in +++ b/cmakeconfig.h.in @@ -15,9 +15,15 @@ /* define if we have the AIX getprotobyname_r() */ #cmakedefine HAVE_AIX_GETPROTOBYNAME_R 1 +/* define if you have the AirPcap API */ +#cmakedefine HAVE_AIRPCAP_API 1 + /* Define to 1 if you have the `asprintf' function. */ #cmakedefine HAVE_ASPRINTF 1 +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_CONFIG_HAIKUCONFIG_H 1 + /* define if you have the DAG API */ #cmakedefine HAVE_DAG_API 1 @@ -69,45 +75,21 @@ /* if libnl exists */ #cmakedefine HAVE_LIBNL 1 -/* if libnl exists and is version 2.x */ -#cmakedefine HAVE_LIBNL_2_x 1 - -/* if libnl exists and is version 3.x */ -#cmakedefine HAVE_LIBNL_3_x 1 - -/* libnl has NLE_FAILURE */ -#cmakedefine HAVE_LIBNL_NLE 1 - -/* libnl has new-style socket api */ -#cmakedefine HAVE_LIBNL_SOCKETS 1 - -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_LIMITS_H 1 - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LINUX_COMPILER_H 1 -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_LINUX_ETHTOOL_H 1 - /* define if we have the Linux getnetbyname_r() */ #cmakedefine HAVE_LINUX_GETNETBYNAME_R 1 /* define if we have the Linux getprotobyname_r() */ #cmakedefine HAVE_LINUX_GETPROTOBYNAME_R 1 -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_LINUX_IF_BONDING_H 1 - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LINUX_NET_TSTAMP_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LINUX_SOCKET_H 1 -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_LINUX_SOCKIOS_H 1 - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LINUX_USBDEVICE_FS_H 1 @@ -135,12 +117,12 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_NET_PFILT_H 1 -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_NET_PFVAR_H 1 - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_NET_RAW_H 1 +/* Use OpenSSL */ +#cmakedefine HAVE_OPENSSL 1 + /* if there's an os_proto.h for this platform, to use additional prototypes */ #cmakedefine HAVE_OS_PROTO_H 1 @@ -153,9 +135,6 @@ /* Define to 1 if you have a POSIX-style `strerror_r' function. */ #cmakedefine HAVE_POSIX_STRERROR_R 1 -/* define if net/pfvar.h defines PF_NAT through PF_NORDR */ -#cmakedefine HAVE_PF_NAT_THROUGH_PF_NORDR 1 - /* define if you have the Septel API */ #cmakedefine HAVE_SEPTEL_API 1 @@ -186,9 +165,6 @@ /* Define to 1 if you have the `strerror' function. */ #cmakedefine HAVE_STRERROR 1 -/* Define to 1 if you have the `strerror_s' function. */ -#cmakedefine HAVE_STRERROR_S 1 - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STRINGS_H 1 @@ -216,6 +192,9 @@ /* Define to 1 if `msg_flags' is a member of `struct msghdr'. */ #cmakedefine HAVE_STRUCT_MSGHDR_MSG_FLAGS 1 +/* Define to 1 if the system has the type `struct rte_ether_addr'. */ +#cmakedefine HAVE_STRUCT_RTE_ETHER_ADDR 1 + /* Define to 1 if `hci_channel' is a member of `struct sockaddr_hci'. */ #cmakedefine HAVE_STRUCT_SOCKADDR_HCI_HCI_CHANNEL 1 @@ -228,9 +207,6 @@ /* Define to 1 if `tp_vlan_tci' is a member of `struct tpacket_auxdata'. */ #cmakedefine HAVE_STRUCT_TPACKET_AUXDATA_TP_VLAN_TCI 1 -/* Define to 1 if the system has the type `struct tpacket_stats'. */ -#cmakedefine HAVE_STRUCT_TPACKET_STATS 1 - /* Define to 1 if `bRequestType' is a member of `struct usbdevfs_ctrltransfer'. */ #cmakedefine HAVE_STRUCT_USBDEVFS_CTRLTRANSFER_BREQUESTTYPE 1 @@ -272,7 +248,19 @@ #cmakedefine HAVE_VSNPRINTF 1 /* Define to 1 if you have the `vsyslog' function. */ -#undef HAVE_VSYSLOG +#cmakedefine HAVE_VSYSLOG 1 + +/* Define to 1 if you have the `_wcserror_s' function. */ +#cmakedefine HAVE__WCSERROR_S 1 + +/* define if __atomic_load_n is supported by the compiler */ +#cmakedefine HAVE___ATOMIC_LOAD_N 1 + +/* define if __atomic_store_n is supported by the compiler */ +#cmakedefine HAVE___ATOMIC_STORE_N 1 + +/* Define to 1 if you have the `PacketGetTimestampModes' function. */ +#cmakedefine HAVE_PACKET_GET_TIMESTAMP_MODES 1 /* Define to 1 if you have the `PacketIsLoopbackAdapter' function. */ #cmakedefine HAVE_PACKET_IS_LOOPBACK_ADAPTER 1 @@ -280,12 +268,6 @@ /* IPv6 */ #cmakedefine INET6 1 -/* if unaligned access fails */ -#cmakedefine LBL_ALIGN 1 - -/* path for device for USB sniffing */ -#cmakedefine LINUX_USB_MON_DEV "@LINUX_USB_MON_DEV@" - /* Define to 1 if netinet/ether.h declares `ether_hostton' */ #cmakedefine NETINET_ETHER_H_DECLARES_ETHER_HOSTTON 1 @@ -301,7 +283,7 @@ /* Define to the address where bug reports for this package should be sent. */ #cmakedefine PACKAGE_BUGREPORT 1 -/* Define to the DLL-preferred version string of of this package. */ +/* Define to the DLL-preferred version string of this package. */ #cmakedefine PACKAGE_VERSION_DLL @PACKAGE_VERSION_DLL@ /* Define to the full name of this package. */ @@ -328,24 +310,21 @@ /* support D-Bus sniffing */ #cmakedefine PCAP_SUPPORT_DBUS 1 +/* target host supports DPDK */ +#cmakedefine PCAP_SUPPORT_DPDK 1 + +/* target host supports Linux usbmon for USB sniffing */ +#cmakedefine PCAP_SUPPORT_LINUX_USBMON 1 + /* target host supports netfilter sniffing */ #cmakedefine PCAP_SUPPORT_NETFILTER 1 /* target host supports netmap */ #cmakedefine PCAP_SUPPORT_NETMAP 1 -/* use packet ring capture support on Linux if available */ -#cmakedefine PCAP_SUPPORT_PACKET_RING 1 - /* target host supports RDMA sniffing */ #cmakedefine PCAP_SUPPORT_RDMASNIFF 1 -/* target host supports USB sniffing */ -#cmakedefine PCAP_SUPPORT_USB 1 - -/* include ACN support */ -#cmakedefine SITA 1 - /* Define to 1 if you have the ANSI C header files. */ #cmakedefine STDC_HEADERS 1 diff --git a/config.guess b/config.guess index 2b79f6d837b9..a419d8643b62 100755 --- a/config.guess +++ b/config.guess @@ -1,12 +1,14 @@ #! /bin/sh # Attempt to guess a canonical system name. -# Copyright 1992-2018 Free Software Foundation, Inc. +# Copyright 1992-2022 Free Software Foundation, Inc. -timestamp='2018-07-06' +# shellcheck disable=SC2006,SC2268 # see below for rationale + +timestamp='2022-08-01' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or +# the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but @@ -27,11 +29,19 @@ timestamp='2018-07-06' # Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: -# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess +# https://git.savannah.gnu.org/cgit/config.git/plain/config.guess # # Please send patches to . +# The "shellcheck disable" line above the timestamp inhibits complaints +# about features and limitations of the classic Bourne shell that were +# superseded or lifted in POSIX. However, this script identifies a wide +# variety of pre-POSIX systems that do not have POSIX shells at all, and +# even some reasonably current systems (Solaris 10 as case-in-point) still +# have a pre-POSIX /bin/sh. + + me=`echo "$0" | sed -e 's,.*/,,'` usage="\ @@ -50,7 +60,7 @@ version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. -Copyright 1992-2018 Free Software Foundation, Inc. +Copyright 1992-2022 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -84,7 +94,8 @@ if test $# != 0; then exit 1 fi -trap 'exit 1' 1 2 15 +# Just in case it came from the environment. +GUESS= # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires @@ -96,73 +107,90 @@ trap 'exit 1' 1 2 15 # Portable tmp directory creation inspired by the Autoconf team. -set_cc_for_build=' -trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; -trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; -: ${TMPDIR=/tmp} ; - { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || - { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp 2>/dev/null) ; } || - { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || - { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; -dummy=$tmp/dummy ; -tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; -case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in - ,,) echo "int x;" > "$dummy.c" ; - for c in cc gcc c89 c99 ; do - if ($c -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then - CC_FOR_BUILD="$c"; break ; - fi ; - done ; - if test x"$CC_FOR_BUILD" = x ; then - CC_FOR_BUILD=no_compiler_found ; - fi - ;; - ,,*) CC_FOR_BUILD=$CC ;; - ,*,*) CC_FOR_BUILD=$HOST_CC ;; -esac ; set_cc_for_build= ;' +tmp= +# shellcheck disable=SC2172 +trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15 + +set_cc_for_build() { + # prevent multiple calls if $tmp is already set + test "$tmp" && return 0 + : "${TMPDIR=/tmp}" + # shellcheck disable=SC2039,SC3028 + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } + dummy=$tmp/dummy + case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in + ,,) echo "int x;" > "$dummy.c" + for driver in cc gcc c89 c99 ; do + if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then + CC_FOR_BUILD=$driver + break + fi + done + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; + esac +} # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) -if (test -f /.attbin/uname) >/dev/null 2>&1 ; then +if test -f /.attbin/uname ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown -UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown -case "$UNAME_SYSTEM" in +case $UNAME_SYSTEM in Linux|GNU|GNU/*) - # If the system lacks a compiler, then just pick glibc. - # We could probably try harder. - LIBC=gnu + LIBC=unknown - eval "$set_cc_for_build" + set_cc_for_build cat <<-EOF > "$dummy.c" #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc - #else + #elif defined(__GLIBC__) LIBC=gnu + #else + #include + /* First heuristic to detect musl libc. */ + #ifdef __DEFINED_va_list + LIBC=musl + #endif #endif EOF - eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`" + cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` + eval "$cc_set_libc" - # If ldd exists, use it to detect musl libc. - if command -v ldd >/dev/null && \ - ldd --version 2>&1 | grep -q ^musl - then - LIBC=musl + # Second heuristic to detect musl libc. + if [ "$LIBC" = unknown ] && + command -v ldd >/dev/null && + ldd --version 2>&1 | grep -q ^musl; then + LIBC=musl + fi + + # If the system lacks a compiler, then just pick glibc. + # We could probably try harder. + if [ "$LIBC" = unknown ]; then + LIBC=gnu fi ;; esac # Note: order is significant - the case branches are not exclusive. -case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in +case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, @@ -174,12 +202,12 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". - sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ - "/sbin/$sysctl" 2>/dev/null || \ - "/usr/sbin/$sysctl" 2>/dev/null || \ + /sbin/sysctl -n hw.machine_arch 2>/dev/null || \ + /usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \ echo unknown)` - case "$UNAME_MACHINE_ARCH" in + case $UNAME_MACHINE_ARCH in + aarch64eb) machine=aarch64_be-unknown ;; armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; @@ -188,18 +216,18 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in earmv*) arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` - machine="${arch}${endian}"-unknown + machine=${arch}${endian}-unknown ;; - *) machine="$UNAME_MACHINE_ARCH"-unknown ;; + *) machine=$UNAME_MACHINE_ARCH-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently (or will in the future) and ABI. - case "$UNAME_MACHINE_ARCH" in + case $UNAME_MACHINE_ARCH in earm*) os=netbsdelf ;; arm*|i386|m68k|ns32k|sh3*|sparc|vax) - eval "$set_cc_for_build" + set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then @@ -215,7 +243,7 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in ;; esac # Determine ABI tags. - case "$UNAME_MACHINE_ARCH" in + case $UNAME_MACHINE_ARCH in earm*) expr='s/^earmv[0-9]/-eabi/;s/eb$//' abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` @@ -226,7 +254,7 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. - case "$UNAME_VERSION" in + case $UNAME_VERSION in Debian*) release='-gnu' ;; @@ -237,45 +265,57 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. - echo "$machine-${os}${release}${abi-}" - exit ;; + GUESS=$machine-${os}${release}${abi-} + ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` - echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE" - exit ;; + GUESS=$UNAME_MACHINE_ARCH-unknown-bitrig$UNAME_RELEASE + ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` - echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE" - exit ;; + GUESS=$UNAME_MACHINE_ARCH-unknown-openbsd$UNAME_RELEASE + ;; + *:SecBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/SecBSD.//'` + GUESS=$UNAME_MACHINE_ARCH-unknown-secbsd$UNAME_RELEASE + ;; *:LibertyBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` - echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE" - exit ;; + GUESS=$UNAME_MACHINE_ARCH-unknown-libertybsd$UNAME_RELEASE + ;; *:MidnightBSD:*:*) - echo "$UNAME_MACHINE"-unknown-midnightbsd"$UNAME_RELEASE" - exit ;; + GUESS=$UNAME_MACHINE-unknown-midnightbsd$UNAME_RELEASE + ;; *:ekkoBSD:*:*) - echo "$UNAME_MACHINE"-unknown-ekkobsd"$UNAME_RELEASE" - exit ;; + GUESS=$UNAME_MACHINE-unknown-ekkobsd$UNAME_RELEASE + ;; *:SolidBSD:*:*) - echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE" - exit ;; + GUESS=$UNAME_MACHINE-unknown-solidbsd$UNAME_RELEASE + ;; + *:OS108:*:*) + GUESS=$UNAME_MACHINE-unknown-os108_$UNAME_RELEASE + ;; macppc:MirBSD:*:*) - echo powerpc-unknown-mirbsd"$UNAME_RELEASE" - exit ;; + GUESS=powerpc-unknown-mirbsd$UNAME_RELEASE + ;; *:MirBSD:*:*) - echo "$UNAME_MACHINE"-unknown-mirbsd"$UNAME_RELEASE" - exit ;; + GUESS=$UNAME_MACHINE-unknown-mirbsd$UNAME_RELEASE + ;; *:Sortix:*:*) - echo "$UNAME_MACHINE"-unknown-sortix - exit ;; + GUESS=$UNAME_MACHINE-unknown-sortix + ;; + *:Twizzler:*:*) + GUESS=$UNAME_MACHINE-unknown-twizzler + ;; *:Redox:*:*) - echo "$UNAME_MACHINE"-unknown-redox - exit ;; + GUESS=$UNAME_MACHINE-unknown-redox + ;; mips:OSF1:*.*) - echo mips-dec-osf1 - exit ;; + GUESS=mips-dec-osf1 + ;; alpha:OSF1:*:*) + # Reset EXIT trap before exiting to avoid spurious non-zero exit code. + trap '' 0 case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` @@ -289,7 +329,7 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` - case "$ALPHA_CPU_TYPE" in + case $ALPHA_CPU_TYPE in "EV4 (21064)") UNAME_MACHINE=alpha ;; "EV4.5 (21064)") @@ -326,117 +366,121 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. - echo "$UNAME_MACHINE"-dec-osf"`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`" - # Reset EXIT trap before exiting to avoid spurious non-zero exit code. - exitcode=$? - trap '' 0 - exit $exitcode ;; + OSF_REL=`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + GUESS=$UNAME_MACHINE-dec-osf$OSF_REL + ;; Amiga*:UNIX_System_V:4.0:*) - echo m68k-unknown-sysv4 - exit ;; + GUESS=m68k-unknown-sysv4 + ;; *:[Aa]miga[Oo][Ss]:*:*) - echo "$UNAME_MACHINE"-unknown-amigaos - exit ;; + GUESS=$UNAME_MACHINE-unknown-amigaos + ;; *:[Mm]orph[Oo][Ss]:*:*) - echo "$UNAME_MACHINE"-unknown-morphos - exit ;; + GUESS=$UNAME_MACHINE-unknown-morphos + ;; *:OS/390:*:*) - echo i370-ibm-openedition - exit ;; + GUESS=i370-ibm-openedition + ;; *:z/VM:*:*) - echo s390-ibm-zvmoe - exit ;; + GUESS=s390-ibm-zvmoe + ;; *:OS400:*:*) - echo powerpc-ibm-os400 - exit ;; + GUESS=powerpc-ibm-os400 + ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) - echo arm-acorn-riscix"$UNAME_RELEASE" - exit ;; + GUESS=arm-acorn-riscix$UNAME_RELEASE + ;; arm*:riscos:*:*|arm*:RISCOS:*:*) - echo arm-unknown-riscos - exit ;; + GUESS=arm-unknown-riscos + ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) - echo hppa1.1-hitachi-hiuxmpp - exit ;; + GUESS=hppa1.1-hitachi-hiuxmpp + ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. - if test "`(/bin/universe) 2>/dev/null`" = att ; then - echo pyramid-pyramid-sysv3 - else - echo pyramid-pyramid-bsd - fi - exit ;; + case `(/bin/universe) 2>/dev/null` in + att) GUESS=pyramid-pyramid-sysv3 ;; + *) GUESS=pyramid-pyramid-bsd ;; + esac + ;; NILE*:*:*:dcosx) - echo pyramid-pyramid-svr4 - exit ;; + GUESS=pyramid-pyramid-svr4 + ;; DRS?6000:unix:4.0:6*) - echo sparc-icl-nx6 - exit ;; + GUESS=sparc-icl-nx6 + ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in - sparc) echo sparc-icl-nx7; exit ;; - esac ;; + sparc) GUESS=sparc-icl-nx7 ;; + esac + ;; s390x:SunOS:*:*) - echo "$UNAME_MACHINE"-ibm-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=$UNAME_MACHINE-ibm-solaris2$SUN_REL + ;; sun4H:SunOS:5.*:*) - echo sparc-hal-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=sparc-hal-solaris2$SUN_REL + ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) - echo sparc-sun-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=sparc-sun-solaris2$SUN_REL + ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) - echo i386-pc-auroraux"$UNAME_RELEASE" - exit ;; + GUESS=i386-pc-auroraux$UNAME_RELEASE + ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) - eval "$set_cc_for_build" + set_cc_for_build SUN_ARCH=i386 # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. - if [ "$CC_FOR_BUILD" != no_compiler_found ]; then + if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + (CCOPTS="" $CC_FOR_BUILD -m64 -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH=x86_64 fi fi - echo "$SUN_ARCH"-pc-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=$SUN_ARCH-pc-solaris2$SUN_REL + ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. - echo sparc-sun-solaris3"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=sparc-sun-solaris3$SUN_REL + ;; sun4*:SunOS:*:*) - case "`/usr/bin/arch -k`" in + case `/usr/bin/arch -k` in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. - echo sparc-sun-sunos"`echo "$UNAME_RELEASE"|sed -e 's/-/_/'`" - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'` + GUESS=sparc-sun-sunos$SUN_REL + ;; sun3*:SunOS:*:*) - echo m68k-sun-sunos"$UNAME_RELEASE" - exit ;; + GUESS=m68k-sun-sunos$UNAME_RELEASE + ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 - case "`/bin/arch`" in + case `/bin/arch` in sun3) - echo m68k-sun-sunos"$UNAME_RELEASE" + GUESS=m68k-sun-sunos$UNAME_RELEASE ;; sun4) - echo sparc-sun-sunos"$UNAME_RELEASE" + GUESS=sparc-sun-sunos$UNAME_RELEASE ;; esac - exit ;; + ;; aushp:SunOS:*:*) - echo sparc-auspex-sunos"$UNAME_RELEASE" - exit ;; + GUESS=sparc-auspex-sunos$UNAME_RELEASE + ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor @@ -446,43 +490,43 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint"$UNAME_RELEASE" - exit ;; + GUESS=m68k-atari-mint$UNAME_RELEASE + ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint"$UNAME_RELEASE" - exit ;; + GUESS=m68k-atari-mint$UNAME_RELEASE + ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) - echo m68k-atari-mint"$UNAME_RELEASE" - exit ;; + GUESS=m68k-atari-mint$UNAME_RELEASE + ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) - echo m68k-milan-mint"$UNAME_RELEASE" - exit ;; + GUESS=m68k-milan-mint$UNAME_RELEASE + ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) - echo m68k-hades-mint"$UNAME_RELEASE" - exit ;; + GUESS=m68k-hades-mint$UNAME_RELEASE + ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) - echo m68k-unknown-mint"$UNAME_RELEASE" - exit ;; + GUESS=m68k-unknown-mint$UNAME_RELEASE + ;; m68k:machten:*:*) - echo m68k-apple-machten"$UNAME_RELEASE" - exit ;; + GUESS=m68k-apple-machten$UNAME_RELEASE + ;; powerpc:machten:*:*) - echo powerpc-apple-machten"$UNAME_RELEASE" - exit ;; + GUESS=powerpc-apple-machten$UNAME_RELEASE + ;; RISC*:Mach:*:*) - echo mips-dec-mach_bsd4.3 - exit ;; + GUESS=mips-dec-mach_bsd4.3 + ;; RISC*:ULTRIX:*:*) - echo mips-dec-ultrix"$UNAME_RELEASE" - exit ;; + GUESS=mips-dec-ultrix$UNAME_RELEASE + ;; VAX*:ULTRIX*:*:*) - echo vax-dec-ultrix"$UNAME_RELEASE" - exit ;; + GUESS=vax-dec-ultrix$UNAME_RELEASE + ;; 2020:CLIX:*:* | 2430:CLIX:*:*) - echo clipper-intergraph-clix"$UNAME_RELEASE" - exit ;; + GUESS=clipper-intergraph-clix$UNAME_RELEASE + ;; mips:*:*:UMIPS | mips:*:*:RISCos) - eval "$set_cc_for_build" + set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #ifdef __cplusplus #include /* for printf() prototype */ @@ -508,78 +552,79 @@ EOF dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`"$dummy" "$dummyarg"` && { echo "$SYSTEM_NAME"; exit; } - echo mips-mips-riscos"$UNAME_RELEASE" - exit ;; + GUESS=mips-mips-riscos$UNAME_RELEASE + ;; Motorola:PowerMAX_OS:*:*) - echo powerpc-motorola-powermax - exit ;; + GUESS=powerpc-motorola-powermax + ;; Motorola:*:4.3:PL8-*) - echo powerpc-harris-powermax - exit ;; + GUESS=powerpc-harris-powermax + ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) - echo powerpc-harris-powermax - exit ;; + GUESS=powerpc-harris-powermax + ;; Night_Hawk:Power_UNIX:*:*) - echo powerpc-harris-powerunix - exit ;; + GUESS=powerpc-harris-powerunix + ;; m88k:CX/UX:7*:*) - echo m88k-harris-cxux7 - exit ;; + GUESS=m88k-harris-cxux7 + ;; m88k:*:4*:R4*) - echo m88k-motorola-sysv4 - exit ;; + GUESS=m88k-motorola-sysv4 + ;; m88k:*:3*:R3*) - echo m88k-motorola-sysv3 - exit ;; + GUESS=m88k-motorola-sysv3 + ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` - if [ "$UNAME_PROCESSOR" = mc88100 ] || [ "$UNAME_PROCESSOR" = mc88110 ] + if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110 then - if [ "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx ] || \ - [ "$TARGET_BINARY_INTERFACE"x = x ] + if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \ + test "$TARGET_BINARY_INTERFACE"x = x then - echo m88k-dg-dgux"$UNAME_RELEASE" + GUESS=m88k-dg-dgux$UNAME_RELEASE else - echo m88k-dg-dguxbcs"$UNAME_RELEASE" + GUESS=m88k-dg-dguxbcs$UNAME_RELEASE fi else - echo i586-dg-dgux"$UNAME_RELEASE" + GUESS=i586-dg-dgux$UNAME_RELEASE fi - exit ;; + ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) - echo m88k-dolphin-sysv3 - exit ;; + GUESS=m88k-dolphin-sysv3 + ;; M88*:*:R3*:*) # Delta 88k system running SVR3 - echo m88k-motorola-sysv3 - exit ;; + GUESS=m88k-motorola-sysv3 + ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) - echo m88k-tektronix-sysv3 - exit ;; + GUESS=m88k-tektronix-sysv3 + ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) - echo m68k-tektronix-bsd - exit ;; + GUESS=m68k-tektronix-bsd + ;; *:IRIX*:*:*) - echo mips-sgi-irix"`echo "$UNAME_RELEASE"|sed -e 's/-/_/g'`" - exit ;; + IRIX_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/g'` + GUESS=mips-sgi-irix$IRIX_REL + ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. - echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id - exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + GUESS=romp-ibm-aix # uname -m gives an 8 hex-code CPU id + ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) - echo i386-ibm-aix - exit ;; + GUESS=i386-ibm-aix + ;; ia64:AIX:*:*) - if [ -x /usr/bin/oslevel ] ; then + if test -x /usr/bin/oslevel ; then IBM_REV=`/usr/bin/oslevel` else - IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" + IBM_REV=$UNAME_VERSION.$UNAME_RELEASE fi - echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV" - exit ;; + GUESS=$UNAME_MACHINE-ibm-aix$IBM_REV + ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then - eval "$set_cc_for_build" + set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #include @@ -593,16 +638,16 @@ EOF EOF if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` then - echo "$SYSTEM_NAME" + GUESS=$SYSTEM_NAME else - echo rs6000-ibm-aix3.2.5 + GUESS=rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then - echo rs6000-ibm-aix3.2.4 + GUESS=rs6000-ibm-aix3.2.4 else - echo rs6000-ibm-aix3.2 + GUESS=rs6000-ibm-aix3.2 fi - exit ;; + ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then @@ -610,57 +655,57 @@ EOF else IBM_ARCH=powerpc fi - if [ -x /usr/bin/lslpp ] ; then - IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | + if test -x /usr/bin/lslpp ; then + IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | \ awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else - IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" + IBM_REV=$UNAME_VERSION.$UNAME_RELEASE fi - echo "$IBM_ARCH"-ibm-aix"$IBM_REV" - exit ;; + GUESS=$IBM_ARCH-ibm-aix$IBM_REV + ;; *:AIX:*:*) - echo rs6000-ibm-aix - exit ;; + GUESS=rs6000-ibm-aix + ;; ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) - echo romp-ibm-bsd4.4 - exit ;; + GUESS=romp-ibm-bsd4.4 + ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and - echo romp-ibm-bsd"$UNAME_RELEASE" # 4.3 with uname added to - exit ;; # report: romp-ibm BSD 4.3 + GUESS=romp-ibm-bsd$UNAME_RELEASE # 4.3 with uname added to + ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) - echo rs6000-bull-bosx - exit ;; + GUESS=rs6000-bull-bosx + ;; DPX/2?00:B.O.S.:*:*) - echo m68k-bull-sysv3 - exit ;; + GUESS=m68k-bull-sysv3 + ;; 9000/[34]??:4.3bsd:1.*:*) - echo m68k-hp-bsd - exit ;; + GUESS=m68k-hp-bsd + ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) - echo m68k-hp-bsd4.4 - exit ;; + GUESS=m68k-hp-bsd4.4 + ;; 9000/[34678]??:HP-UX:*:*) - HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` - case "$UNAME_MACHINE" in + HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` + case $UNAME_MACHINE in 9000/31?) HP_ARCH=m68000 ;; 9000/[34]??) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) - if [ -x /usr/bin/getconf ]; then + if test -x /usr/bin/getconf; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` - case "$sc_cpu_version" in + case $sc_cpu_version in 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 - case "$sc_kernel_bits" in + case $sc_kernel_bits in 32) HP_ARCH=hppa2.0n ;; 64) HP_ARCH=hppa2.0w ;; '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 esac ;; esac fi - if [ "$HP_ARCH" = "" ]; then - eval "$set_cc_for_build" + if test "$HP_ARCH" = ""; then + set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #define _HPUX_SOURCE @@ -698,9 +743,9 @@ EOF test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac - if [ "$HP_ARCH" = hppa2.0w ] + if test "$HP_ARCH" = hppa2.0w then - eval "$set_cc_for_build" + set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler @@ -719,14 +764,14 @@ EOF HP_ARCH=hppa64 fi fi - echo "$HP_ARCH"-hp-hpux"$HPUX_REV" - exit ;; + GUESS=$HP_ARCH-hp-hpux$HPUX_REV + ;; ia64:HP-UX:*:*) - HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` - echo ia64-hp-hpux"$HPUX_REV" - exit ;; + HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` + GUESS=ia64-hp-hpux$HPUX_REV + ;; 3050*:HI-UX:*:*) - eval "$set_cc_for_build" + set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #include int @@ -754,36 +799,36 @@ EOF EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } - echo unknown-hitachi-hiuxwe2 - exit ;; + GUESS=unknown-hitachi-hiuxwe2 + ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) - echo hppa1.1-hp-bsd - exit ;; + GUESS=hppa1.1-hp-bsd + ;; 9000/8??:4.3bsd:*:*) - echo hppa1.0-hp-bsd - exit ;; + GUESS=hppa1.0-hp-bsd + ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) - echo hppa1.0-hp-mpeix - exit ;; + GUESS=hppa1.0-hp-mpeix + ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) - echo hppa1.1-hp-osf - exit ;; + GUESS=hppa1.1-hp-osf + ;; hp8??:OSF1:*:*) - echo hppa1.0-hp-osf - exit ;; + GUESS=hppa1.0-hp-osf + ;; i*86:OSF1:*:*) - if [ -x /usr/sbin/sysversion ] ; then - echo "$UNAME_MACHINE"-unknown-osf1mk + if test -x /usr/sbin/sysversion ; then + GUESS=$UNAME_MACHINE-unknown-osf1mk else - echo "$UNAME_MACHINE"-unknown-osf1 + GUESS=$UNAME_MACHINE-unknown-osf1 fi - exit ;; + ;; parisc*:Lites*:*:*) - echo hppa1.1-hp-lites - exit ;; + GUESS=hppa1.1-hp-lites + ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) - echo c1-convex-bsd - exit ;; + GUESS=c1-convex-bsd + ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd @@ -791,17 +836,18 @@ EOF fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) - echo c34-convex-bsd - exit ;; + GUESS=c34-convex-bsd + ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) - echo c38-convex-bsd - exit ;; + GUESS=c38-convex-bsd + ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) - echo c4-convex-bsd - exit ;; + GUESS=c4-convex-bsd + ;; CRAY*Y-MP:*:*:*) - echo ymp-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' - exit ;; + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=ymp-cray-unicos$CRAY_REL + ;; CRAY*[A-Z]90:*:*:*) echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ @@ -809,103 +855,129 @@ EOF -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) - echo t90-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' - exit ;; + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=t90-cray-unicos$CRAY_REL + ;; CRAY*T3E:*:*:*) - echo alphaev5-cray-unicosmk"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' - exit ;; + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=alphaev5-cray-unicosmk$CRAY_REL + ;; CRAY*SV1:*:*:*) - echo sv1-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' - exit ;; + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=sv1-cray-unicos$CRAY_REL + ;; *:UNICOS/mp:*:*) - echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' - exit ;; + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=craynv-cray-unicosmp$CRAY_REL + ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` - echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit ;; + GUESS=${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} + ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` - echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit ;; + GUESS=sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} + ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) - echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE" - exit ;; + GUESS=$UNAME_MACHINE-pc-bsdi$UNAME_RELEASE + ;; sparc*:BSD/OS:*:*) - echo sparc-unknown-bsdi"$UNAME_RELEASE" - exit ;; + GUESS=sparc-unknown-bsdi$UNAME_RELEASE + ;; *:BSD/OS:*:*) - echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE" - exit ;; + GUESS=$UNAME_MACHINE-unknown-bsdi$UNAME_RELEASE + ;; + arm:FreeBSD:*:*) + UNAME_PROCESSOR=`uname -p` + set_cc_for_build + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabi + else + FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf + fi + ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` - case "$UNAME_PROCESSOR" in + case $UNAME_PROCESSOR in amd64) UNAME_PROCESSOR=x86_64 ;; i386) UNAME_PROCESSOR=i586 ;; esac - echo "$UNAME_PROCESSOR"-unknown-freebsd"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" - exit ;; + FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL + ;; i*:CYGWIN*:*) - echo "$UNAME_MACHINE"-pc-cygwin - exit ;; + GUESS=$UNAME_MACHINE-pc-cygwin + ;; *:MINGW64*:*) - echo "$UNAME_MACHINE"-pc-mingw64 - exit ;; + GUESS=$UNAME_MACHINE-pc-mingw64 + ;; *:MINGW*:*) - echo "$UNAME_MACHINE"-pc-mingw32 - exit ;; + GUESS=$UNAME_MACHINE-pc-mingw32 + ;; *:MSYS*:*) - echo "$UNAME_MACHINE"-pc-msys - exit ;; + GUESS=$UNAME_MACHINE-pc-msys + ;; i*:PW*:*) - echo "$UNAME_MACHINE"-pc-pw32 - exit ;; + GUESS=$UNAME_MACHINE-pc-pw32 + ;; + *:SerenityOS:*:*) + GUESS=$UNAME_MACHINE-pc-serenity + ;; *:Interix*:*) - case "$UNAME_MACHINE" in + case $UNAME_MACHINE in x86) - echo i586-pc-interix"$UNAME_RELEASE" - exit ;; + GUESS=i586-pc-interix$UNAME_RELEASE + ;; authenticamd | genuineintel | EM64T) - echo x86_64-unknown-interix"$UNAME_RELEASE" - exit ;; + GUESS=x86_64-unknown-interix$UNAME_RELEASE + ;; IA64) - echo ia64-unknown-interix"$UNAME_RELEASE" - exit ;; + GUESS=ia64-unknown-interix$UNAME_RELEASE + ;; esac ;; i*:UWIN*:*) - echo "$UNAME_MACHINE"-pc-uwin - exit ;; + GUESS=$UNAME_MACHINE-pc-uwin + ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) - echo x86_64-unknown-cygwin - exit ;; + GUESS=x86_64-pc-cygwin + ;; prep*:SunOS:5.*:*) - echo powerpcle-unknown-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=powerpcle-unknown-solaris2$SUN_REL + ;; *:GNU:*:*) # the GNU system - echo "`echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,'`-unknown-$LIBC`echo "$UNAME_RELEASE"|sed -e 's,/.*$,,'`" - exit ;; + GNU_ARCH=`echo "$UNAME_MACHINE" | sed -e 's,[-/].*$,,'` + GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's,/.*$,,'` + GUESS=$GNU_ARCH-unknown-$LIBC$GNU_REL + ;; *:GNU/*:*:*) # other systems with GNU libc and userland - echo "$UNAME_MACHINE-unknown-`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC" - exit ;; + GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"` + GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC + ;; *:Minix:*:*) - echo "$UNAME_MACHINE"-unknown-minix - exit ;; + GUESS=$UNAME_MACHINE-unknown-minix + ;; aarch64:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; alpha:Linux:*:*) - case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; @@ -916,183 +988,236 @@ EOF esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC=gnulibc1 ; fi - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - arc:Linux:*:* | arceb:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + arc:Linux:*:* | arceb:Linux:*:* | arc32:Linux:*:* | arc64:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; arm*:Linux:*:*) - eval "$set_cc_for_build" + set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabi + GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabi else - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabihf + GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabihf fi fi - exit ;; + ;; avr32*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; cris:Linux:*:*) - echo "$UNAME_MACHINE"-axis-linux-"$LIBC" - exit ;; + GUESS=$UNAME_MACHINE-axis-linux-$LIBC + ;; crisv32:Linux:*:*) - echo "$UNAME_MACHINE"-axis-linux-"$LIBC" - exit ;; + GUESS=$UNAME_MACHINE-axis-linux-$LIBC + ;; e2k:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; frv:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; hexagon:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; i*86:Linux:*:*) - echo "$UNAME_MACHINE"-pc-linux-"$LIBC" - exit ;; + GUESS=$UNAME_MACHINE-pc-linux-$LIBC + ;; ia64:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; k1om:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + loongarch32:Linux:*:* | loongarch64:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; m32r*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; m68*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; mips:Linux:*:* | mips64:Linux:*:*) - eval "$set_cc_for_build" + set_cc_for_build + IS_GLIBC=0 + test x"${LIBC}" = xgnu && IS_GLIBC=1 sed 's/^ //' << EOF > "$dummy.c" #undef CPU - #undef ${UNAME_MACHINE} - #undef ${UNAME_MACHINE}el + #undef mips + #undef mipsel + #undef mips64 + #undef mips64el + #if ${IS_GLIBC} && defined(_ABI64) + LIBCABI=gnuabi64 + #else + #if ${IS_GLIBC} && defined(_ABIN32) + LIBCABI=gnuabin32 + #else + LIBCABI=${LIBC} + #endif + #endif + + #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 + CPU=mipsisa64r6 + #else + #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 + CPU=mipsisa32r6 + #else + #if defined(__mips64) + CPU=mips64 + #else + CPU=mips + #endif + #endif + #endif + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) - CPU=${UNAME_MACHINE}el + MIPS_ENDIAN=el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) - CPU=${UNAME_MACHINE} + MIPS_ENDIAN= #else - CPU= + MIPS_ENDIAN= #endif #endif EOF - eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU'`" - test "x$CPU" != x && { echo "$CPU-unknown-linux-$LIBC"; exit; } + cc_set_vars=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'` + eval "$cc_set_vars" + test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; } ;; mips64el:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; openrisc*:Linux:*:*) - echo or1k-unknown-linux-"$LIBC" - exit ;; + GUESS=or1k-unknown-linux-$LIBC + ;; or32:Linux:*:* | or1k*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; padre:Linux:*:*) - echo sparc-unknown-linux-"$LIBC" - exit ;; + GUESS=sparc-unknown-linux-$LIBC + ;; parisc64:Linux:*:* | hppa64:Linux:*:*) - echo hppa64-unknown-linux-"$LIBC" - exit ;; + GUESS=hppa64-unknown-linux-$LIBC + ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in - PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;; - PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;; - *) echo hppa-unknown-linux-"$LIBC" ;; + PA7*) GUESS=hppa1.1-unknown-linux-$LIBC ;; + PA8*) GUESS=hppa2.0-unknown-linux-$LIBC ;; + *) GUESS=hppa-unknown-linux-$LIBC ;; esac - exit ;; + ;; ppc64:Linux:*:*) - echo powerpc64-unknown-linux-"$LIBC" - exit ;; + GUESS=powerpc64-unknown-linux-$LIBC + ;; ppc:Linux:*:*) - echo powerpc-unknown-linux-"$LIBC" - exit ;; + GUESS=powerpc-unknown-linux-$LIBC + ;; ppc64le:Linux:*:*) - echo powerpc64le-unknown-linux-"$LIBC" - exit ;; + GUESS=powerpc64le-unknown-linux-$LIBC + ;; ppcle:Linux:*:*) - echo powerpcle-unknown-linux-"$LIBC" - exit ;; - riscv32:Linux:*:* | riscv64:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; + GUESS=powerpcle-unknown-linux-$LIBC + ;; + riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; s390:Linux:*:* | s390x:Linux:*:*) - echo "$UNAME_MACHINE"-ibm-linux-"$LIBC" - exit ;; + GUESS=$UNAME_MACHINE-ibm-linux-$LIBC + ;; sh64*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; sh*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; sparc:Linux:*:* | sparc64:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; tile*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; vax:Linux:*:*) - echo "$UNAME_MACHINE"-dec-linux-"$LIBC" - exit ;; + GUESS=$UNAME_MACHINE-dec-linux-$LIBC + ;; x86_64:Linux:*:*) - echo "$UNAME_MACHINE"-pc-linux-"$LIBC" - exit ;; + set_cc_for_build + CPU=$UNAME_MACHINE + LIBCABI=$LIBC + if test "$CC_FOR_BUILD" != no_compiler_found; then + ABI=64 + sed 's/^ //' << EOF > "$dummy.c" + #ifdef __i386__ + ABI=x86 + #else + #ifdef __ILP32__ + ABI=x32 + #endif + #endif +EOF + cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'` + eval "$cc_set_abi" + case $ABI in + x86) CPU=i686 ;; + x32) LIBCABI=${LIBC}x32 ;; + esac + fi + GUESS=$CPU-pc-linux-$LIBCABI + ;; xtensa*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. - echo i386-sequent-sysv4 - exit ;; + GUESS=i386-sequent-sysv4 + ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. - echo "$UNAME_MACHINE"-pc-sysv4.2uw"$UNAME_VERSION" - exit ;; + GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION + ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. - echo "$UNAME_MACHINE"-pc-os2-emx - exit ;; + GUESS=$UNAME_MACHINE-pc-os2-emx + ;; i*86:XTS-300:*:STOP) - echo "$UNAME_MACHINE"-unknown-stop - exit ;; + GUESS=$UNAME_MACHINE-unknown-stop + ;; i*86:atheos:*:*) - echo "$UNAME_MACHINE"-unknown-atheos - exit ;; + GUESS=$UNAME_MACHINE-unknown-atheos + ;; i*86:syllable:*:*) - echo "$UNAME_MACHINE"-pc-syllable - exit ;; + GUESS=$UNAME_MACHINE-pc-syllable + ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) - echo i386-unknown-lynxos"$UNAME_RELEASE" - exit ;; + GUESS=i386-unknown-lynxos$UNAME_RELEASE + ;; i*86:*DOS:*:*) - echo "$UNAME_MACHINE"-pc-msdosdjgpp - exit ;; + GUESS=$UNAME_MACHINE-pc-msdosdjgpp + ;; i*86:*:4.*:*) UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then - echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL" + GUESS=$UNAME_MACHINE-univel-sysv$UNAME_REL else - echo "$UNAME_MACHINE"-pc-sysv"$UNAME_REL" + GUESS=$UNAME_MACHINE-pc-sysv$UNAME_REL fi - exit ;; + ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in @@ -1100,12 +1225,12 @@ EOF *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac - echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}{$UNAME_VERSION}" - exit ;; + GUESS=$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 @@ -1115,11 +1240,11 @@ EOF && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 - echo "$UNAME_MACHINE"-pc-sco"$UNAME_REL" + GUESS=$UNAME_MACHINE-pc-sco$UNAME_REL else - echo "$UNAME_MACHINE"-pc-sysv32 + GUESS=$UNAME_MACHINE-pc-sysv32 fi - exit ;; + ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about @@ -1127,31 +1252,31 @@ EOF # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configure will decide that # this is a cross-build. - echo i586-pc-msdosdjgpp - exit ;; + GUESS=i586-pc-msdosdjgpp + ;; Intel:Mach:3*:*) - echo i386-pc-mach3 - exit ;; + GUESS=i386-pc-mach3 + ;; paragon:*:*:*) - echo i860-intel-osf1 - exit ;; + GUESS=i860-intel-osf1 + ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then - echo i860-stardent-sysv"$UNAME_RELEASE" # Stardent Vistra i860-SVR4 + GUESS=i860-stardent-sysv$UNAME_RELEASE # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. - echo i860-unknown-sysv"$UNAME_RELEASE" # Unknown i860-SVR4 + GUESS=i860-unknown-sysv$UNAME_RELEASE # Unknown i860-SVR4 fi - exit ;; + ;; mini*:CTIX:SYS*5:*) # "miniframe" - echo m68010-convergent-sysv - exit ;; + GUESS=m68010-convergent-sysv + ;; mc68k:UNIX:SYSTEM5:3.51m) - echo m68k-convergent-sysv - exit ;; + GUESS=m68k-convergent-sysv + ;; M680?0:D-NIX:5.3:*) - echo m68k-diab-dnix - exit ;; + GUESS=m68k-diab-dnix + ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) @@ -1176,249 +1301,407 @@ EOF /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) - echo m68k-unknown-lynxos"$UNAME_RELEASE" - exit ;; + GUESS=m68k-unknown-lynxos$UNAME_RELEASE + ;; mc68030:UNIX_System_V:4.*:*) - echo m68k-atari-sysv4 - exit ;; + GUESS=m68k-atari-sysv4 + ;; TSUNAMI:LynxOS:2.*:*) - echo sparc-unknown-lynxos"$UNAME_RELEASE" - exit ;; + GUESS=sparc-unknown-lynxos$UNAME_RELEASE + ;; rs6000:LynxOS:2.*:*) - echo rs6000-unknown-lynxos"$UNAME_RELEASE" - exit ;; + GUESS=rs6000-unknown-lynxos$UNAME_RELEASE + ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) - echo powerpc-unknown-lynxos"$UNAME_RELEASE" - exit ;; + GUESS=powerpc-unknown-lynxos$UNAME_RELEASE + ;; SM[BE]S:UNIX_SV:*:*) - echo mips-dde-sysv"$UNAME_RELEASE" - exit ;; + GUESS=mips-dde-sysv$UNAME_RELEASE + ;; RM*:ReliantUNIX-*:*:*) - echo mips-sni-sysv4 - exit ;; + GUESS=mips-sni-sysv4 + ;; RM*:SINIX-*:*:*) - echo mips-sni-sysv4 - exit ;; + GUESS=mips-sni-sysv4 + ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` - echo "$UNAME_MACHINE"-sni-sysv4 + GUESS=$UNAME_MACHINE-sni-sysv4 else - echo ns32k-sni-sysv + GUESS=ns32k-sni-sysv fi - exit ;; + ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says - echo i586-unisys-sysv4 - exit ;; + GUESS=i586-unisys-sysv4 + ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm - echo hppa1.1-stratus-sysv4 - exit ;; + GUESS=hppa1.1-stratus-sysv4 + ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. - echo i860-stratus-sysv4 - exit ;; + GUESS=i860-stratus-sysv4 + ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. - echo "$UNAME_MACHINE"-stratus-vos - exit ;; + GUESS=$UNAME_MACHINE-stratus-vos + ;; *:VOS:*:*) # From Paul.Green@stratus.com. - echo hppa1.1-stratus-vos - exit ;; + GUESS=hppa1.1-stratus-vos + ;; mc68*:A/UX:*:*) - echo m68k-apple-aux"$UNAME_RELEASE" - exit ;; + GUESS=m68k-apple-aux$UNAME_RELEASE + ;; news*:NEWS-OS:6*:*) - echo mips-sony-newsos6 - exit ;; + GUESS=mips-sony-newsos6 + ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) - if [ -d /usr/nec ]; then - echo mips-nec-sysv"$UNAME_RELEASE" + if test -d /usr/nec; then + GUESS=mips-nec-sysv$UNAME_RELEASE else - echo mips-unknown-sysv"$UNAME_RELEASE" + GUESS=mips-unknown-sysv$UNAME_RELEASE fi - exit ;; + ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. - echo powerpc-be-beos - exit ;; + GUESS=powerpc-be-beos + ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. - echo powerpc-apple-beos - exit ;; + GUESS=powerpc-apple-beos + ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. - echo i586-pc-beos - exit ;; + GUESS=i586-pc-beos + ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. - echo i586-pc-haiku - exit ;; - x86_64:Haiku:*:*) - echo x86_64-unknown-haiku - exit ;; + GUESS=i586-pc-haiku + ;; + ppc:Haiku:*:*) # Haiku running on Apple PowerPC + GUESS=powerpc-apple-haiku + ;; + *:Haiku:*:*) # Haiku modern gcc (not bound by BeOS compat) + GUESS=$UNAME_MACHINE-unknown-haiku + ;; SX-4:SUPER-UX:*:*) - echo sx4-nec-superux"$UNAME_RELEASE" - exit ;; + GUESS=sx4-nec-superux$UNAME_RELEASE + ;; SX-5:SUPER-UX:*:*) - echo sx5-nec-superux"$UNAME_RELEASE" - exit ;; + GUESS=sx5-nec-superux$UNAME_RELEASE + ;; SX-6:SUPER-UX:*:*) - echo sx6-nec-superux"$UNAME_RELEASE" - exit ;; + GUESS=sx6-nec-superux$UNAME_RELEASE + ;; SX-7:SUPER-UX:*:*) - echo sx7-nec-superux"$UNAME_RELEASE" - exit ;; + GUESS=sx7-nec-superux$UNAME_RELEASE + ;; SX-8:SUPER-UX:*:*) - echo sx8-nec-superux"$UNAME_RELEASE" - exit ;; + GUESS=sx8-nec-superux$UNAME_RELEASE + ;; SX-8R:SUPER-UX:*:*) - echo sx8r-nec-superux"$UNAME_RELEASE" - exit ;; + GUESS=sx8r-nec-superux$UNAME_RELEASE + ;; SX-ACE:SUPER-UX:*:*) - echo sxace-nec-superux"$UNAME_RELEASE" - exit ;; + GUESS=sxace-nec-superux$UNAME_RELEASE + ;; Power*:Rhapsody:*:*) - echo powerpc-apple-rhapsody"$UNAME_RELEASE" - exit ;; + GUESS=powerpc-apple-rhapsody$UNAME_RELEASE + ;; *:Rhapsody:*:*) - echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE" - exit ;; + GUESS=$UNAME_MACHINE-apple-rhapsody$UNAME_RELEASE + ;; + arm64:Darwin:*:*) + GUESS=aarch64-apple-darwin$UNAME_RELEASE + ;; *:Darwin:*:*) - UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown - eval "$set_cc_for_build" - if test "$UNAME_PROCESSOR" = unknown ; then - UNAME_PROCESSOR=powerpc + UNAME_PROCESSOR=`uname -p` + case $UNAME_PROCESSOR in + unknown) UNAME_PROCESSOR=powerpc ;; + esac + if command -v xcode-select > /dev/null 2> /dev/null && \ + ! xcode-select --print-path > /dev/null 2> /dev/null ; then + # Avoid executing cc if there is no toolchain installed as + # cc will be a stub that puts up a graphical alert + # prompting the user to install developer tools. + CC_FOR_BUILD=no_compiler_found + else + set_cc_for_build fi - if test "`echo "$UNAME_RELEASE" | sed -e 's/\..*//'`" -le 10 ; then - if [ "$CC_FOR_BUILD" != no_compiler_found ]; then - if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_64BIT_ARCH >/dev/null - then - case $UNAME_PROCESSOR in - i386) UNAME_PROCESSOR=x86_64 ;; - powerpc) UNAME_PROCESSOR=powerpc64 ;; - esac - fi - # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc - if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ - (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_PPC >/dev/null - then - UNAME_PROCESSOR=powerpc - fi + if test "$CC_FOR_BUILD" != no_compiler_found; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi + # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc + if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_PPC >/dev/null + then + UNAME_PROCESSOR=powerpc fi elif test "$UNAME_PROCESSOR" = i386 ; then - # Avoid executing cc on OS X 10.9, as it ships with a stub - # that puts up a graphical alert prompting to install - # developer tools. Any system running Mac OS X 10.7 or - # later (Darwin 11 and later) is required to have a 64-bit - # processor. This is not true of the ARM version of Darwin - # that Apple uses in portable devices. - UNAME_PROCESSOR=x86_64 + # uname -m returns i386 or x86_64 + UNAME_PROCESSOR=$UNAME_MACHINE fi - echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE" - exit ;; + GUESS=$UNAME_PROCESSOR-apple-darwin$UNAME_RELEASE + ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = x86; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi - echo "$UNAME_PROCESSOR"-"$UNAME_MACHINE"-nto-qnx"$UNAME_RELEASE" - exit ;; + GUESS=$UNAME_PROCESSOR-$UNAME_MACHINE-nto-qnx$UNAME_RELEASE + ;; *:QNX:*:4*) - echo i386-pc-qnx - exit ;; + GUESS=i386-pc-qnx + ;; NEO-*:NONSTOP_KERNEL:*:*) - echo neo-tandem-nsk"$UNAME_RELEASE" - exit ;; + GUESS=neo-tandem-nsk$UNAME_RELEASE + ;; NSE-*:NONSTOP_KERNEL:*:*) - echo nse-tandem-nsk"$UNAME_RELEASE" - exit ;; + GUESS=nse-tandem-nsk$UNAME_RELEASE + ;; NSR-*:NONSTOP_KERNEL:*:*) - echo nsr-tandem-nsk"$UNAME_RELEASE" - exit ;; + GUESS=nsr-tandem-nsk$UNAME_RELEASE + ;; NSV-*:NONSTOP_KERNEL:*:*) - echo nsv-tandem-nsk"$UNAME_RELEASE" - exit ;; + GUESS=nsv-tandem-nsk$UNAME_RELEASE + ;; NSX-*:NONSTOP_KERNEL:*:*) - echo nsx-tandem-nsk"$UNAME_RELEASE" - exit ;; + GUESS=nsx-tandem-nsk$UNAME_RELEASE + ;; *:NonStop-UX:*:*) - echo mips-compaq-nonstopux - exit ;; + GUESS=mips-compaq-nonstopux + ;; BS2000:POSIX*:*:*) - echo bs2000-siemens-sysv - exit ;; + GUESS=bs2000-siemens-sysv + ;; DS/*:UNIX_System_V:*:*) - echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE" - exit ;; + GUESS=$UNAME_MACHINE-$UNAME_SYSTEM-$UNAME_RELEASE + ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. - if test "$cputype" = 386; then + if test "${cputype-}" = 386; then UNAME_MACHINE=i386 - else - UNAME_MACHINE="$cputype" + elif test "x${cputype-}" != x; then + UNAME_MACHINE=$cputype fi - echo "$UNAME_MACHINE"-unknown-plan9 - exit ;; + GUESS=$UNAME_MACHINE-unknown-plan9 + ;; *:TOPS-10:*:*) - echo pdp10-unknown-tops10 - exit ;; + GUESS=pdp10-unknown-tops10 + ;; *:TENEX:*:*) - echo pdp10-unknown-tenex - exit ;; + GUESS=pdp10-unknown-tenex + ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) - echo pdp10-dec-tops20 - exit ;; + GUESS=pdp10-dec-tops20 + ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) - echo pdp10-xkl-tops20 - exit ;; + GUESS=pdp10-xkl-tops20 + ;; *:TOPS-20:*:*) - echo pdp10-unknown-tops20 - exit ;; + GUESS=pdp10-unknown-tops20 + ;; *:ITS:*:*) - echo pdp10-unknown-its - exit ;; + GUESS=pdp10-unknown-its + ;; SEI:*:*:SEIUX) - echo mips-sei-seiux"$UNAME_RELEASE" - exit ;; + GUESS=mips-sei-seiux$UNAME_RELEASE + ;; *:DragonFly:*:*) - echo "$UNAME_MACHINE"-unknown-dragonfly"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" - exit ;; + DRAGONFLY_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_MACHINE-unknown-dragonfly$DRAGONFLY_REL + ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` - case "$UNAME_MACHINE" in - A*) echo alpha-dec-vms ; exit ;; - I*) echo ia64-dec-vms ; exit ;; - V*) echo vax-dec-vms ; exit ;; + case $UNAME_MACHINE in + A*) GUESS=alpha-dec-vms ;; + I*) GUESS=ia64-dec-vms ;; + V*) GUESS=vax-dec-vms ;; esac ;; *:XENIX:*:SysV) - echo i386-pc-xenix - exit ;; + GUESS=i386-pc-xenix + ;; i*86:skyos:*:*) - echo "$UNAME_MACHINE"-pc-skyos"`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`" - exit ;; + SKYOS_REL=`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'` + GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL + ;; i*86:rdos:*:*) - echo "$UNAME_MACHINE"-pc-rdos - exit ;; - i*86:AROS:*:*) - echo "$UNAME_MACHINE"-pc-aros - exit ;; + GUESS=$UNAME_MACHINE-pc-rdos + ;; + i*86:Fiwix:*:*) + GUESS=$UNAME_MACHINE-pc-fiwix + ;; + *:AROS:*:*) + GUESS=$UNAME_MACHINE-unknown-aros + ;; x86_64:VMkernel:*:*) - echo "$UNAME_MACHINE"-unknown-esx - exit ;; + GUESS=$UNAME_MACHINE-unknown-esx + ;; amd64:Isilon\ OneFS:*:*) - echo x86_64-unknown-onefs - exit ;; + GUESS=x86_64-unknown-onefs + ;; + *:Unleashed:*:*) + GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE + ;; esac +# Do we have a guess based on uname results? +if test "x$GUESS" != x; then + echo "$GUESS" + exit +fi + +# No uname command or uname output not recognized. +set_cc_for_build +cat > "$dummy.c" < +#include +#endif +#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) +#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) +#include +#if defined(_SIZE_T_) || defined(SIGLOST) +#include +#endif +#endif +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); +#endif + +#if defined (vax) +#if !defined (ultrix) +#include +#if defined (BSD) +#if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +#else +#if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +#else + printf ("vax-dec-bsd\n"); exit (0); +#endif +#endif +#else + printf ("vax-dec-bsd\n"); exit (0); +#endif +#else +#if defined(_SIZE_T_) || defined(SIGLOST) + struct utsname un; + uname (&un); + printf ("vax-dec-ultrix%s\n", un.release); exit (0); +#else + printf ("vax-dec-ultrix\n"); exit (0); +#endif +#endif +#endif +#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) +#if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) +#if defined(_SIZE_T_) || defined(SIGLOST) + struct utsname *un; + uname (&un); + printf ("mips-dec-ultrix%s\n", un.release); exit (0); +#else + printf ("mips-dec-ultrix\n"); exit (0); +#endif +#endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`"$dummy"` && + { echo "$SYSTEM_NAME"; exit; } + +# Apollos put the system type in the environment. +test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; } + echo "$0: unable to guess system type" >&2 -case "$UNAME_MACHINE:$UNAME_SYSTEM" in +case $UNAME_MACHINE:$UNAME_SYSTEM in mips:Linux | mips64:Linux) # If we got here on MIPS GNU/Linux, output extra information. cat >&2 <&2 < header file. */ +#undef HAVE_CONFIG_HAIKUCONFIG_H + /* Define to 1 if you have the header file. */ #undef HAVE_DAGAPI_H @@ -69,8 +72,8 @@ /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H -/* Define to 1 if you have the `dag' library (-ldag). */ -#undef HAVE_LIBDAG +/* Define to 1 if you have the `bsd' library (-lbsd). */ +#undef HAVE_LIBBSD /* if libdlpi exists */ #undef HAVE_LIBDLPI @@ -78,45 +81,21 @@ /* if libnl exists */ #undef HAVE_LIBNL -/* if libnl exists and is version 2.x */ -#undef HAVE_LIBNL_2_x - -/* if libnl exists and is version 3.x */ -#undef HAVE_LIBNL_3_x - -/* libnl has NLE_FAILURE */ -#undef HAVE_LIBNL_NLE - -/* libnl has new-style socket api */ -#undef HAVE_LIBNL_SOCKETS - -/* Define to 1 if you have the header file. */ -#undef HAVE_LIMITS_H - /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_COMPILER_H -/* Define to 1 if you have the header file. */ -#undef HAVE_LINUX_ETHTOOL_H - /* define if we have the Linux getnetbyname_r() */ #undef HAVE_LINUX_GETNETBYNAME_R /* define if we have the Linux getprotobyname_r() */ #undef HAVE_LINUX_GETPROTOBYNAME_R -/* Define to 1 if you have the header file. */ -#undef HAVE_LINUX_IF_BONDING_H - /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_NET_TSTAMP_H /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_SOCKET_H -/* Define to 1 if you have the header file. */ -#undef HAVE_LINUX_SOCKIOS_H - /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_USBDEVICE_FS_H @@ -135,27 +114,33 @@ /* Define to 1 if you have the header file. */ #undef HAVE_NET_ENET_H +/* Define to 1 if you have the header file. */ +#undef HAVE_NET_IF_DL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NET_IF_H + /* Define to 1 if you have the header file. */ #undef HAVE_NET_IF_MEDIA_H +/* Define to 1 if you have the header file. */ +#undef HAVE_NET_IF_TYPES_H + /* Define to 1 if you have the header file. */ #undef HAVE_NET_NIT_H /* Define to 1 if you have the header file. */ #undef HAVE_NET_PFILT_H -/* Define to 1 if you have the header file. */ -#undef HAVE_NET_PFVAR_H - /* Define to 1 if you have the header file. */ #undef HAVE_NET_RAW_H +/* Use OpenSSL */ +#undef HAVE_OPENSSL + /* if there's an os_proto.h for this platform, to use additional prototypes */ #undef HAVE_OS_PROTO_H -/* define if net/pfvar.h defines PF_NAT through PF_NORDR */ -#undef HAVE_PF_NAT_THROUGH_PF_NORDR - /* Define to 1 if you have a POSIX-style `strerror_r' function. */ #undef HAVE_POSIX_STRERROR_R @@ -165,9 +150,6 @@ /* define if you have the Myricom SNF API */ #undef HAVE_SNF_API -/* Define to 1 if you have the `snprintf' function. */ -#undef HAVE_SNPRINTF - /* Define to 1 if the system has the type `socklen_t'. */ #undef HAVE_SOCKLEN_T @@ -189,9 +171,6 @@ /* Define to 1 if you have the `strerror' function. */ #undef HAVE_STRERROR -/* Define to 1 if you have the `strerror_s' function. */ -#undef HAVE_STRERROR_S - /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H @@ -219,6 +198,9 @@ /* Define to 1 if `msg_flags' is a member of `struct msghdr'. */ #undef HAVE_STRUCT_MSGHDR_MSG_FLAGS +/* Define to 1 if the system has the type `struct rte_ether_addr'. */ +#undef HAVE_STRUCT_RTE_ETHER_ADDR + /* Define to 1 if `hci_channel' is a member of `struct sockaddr_hci'. */ #undef HAVE_STRUCT_SOCKADDR_HCI_HCI_CHANNEL @@ -231,9 +213,6 @@ /* Define to 1 if `tp_vlan_tci' is a member of `struct tpacket_auxdata'. */ #undef HAVE_STRUCT_TPACKET_AUXDATA_TP_VLAN_TCI -/* Define to 1 if the system has the type `struct tpacket_stats'. */ -#undef HAVE_STRUCT_TPACKET_STATS - /* Define to 1 if `bRequestType' is a member of `struct usbdevfs_ctrltransfer'. */ #undef HAVE_STRUCT_USBDEVFS_CTRLTRANSFER_BREQUESTTYPE @@ -271,21 +250,21 @@ /* Define to 1 if you have the `vasprintf' function. */ #undef HAVE_VASPRINTF -/* Define to 1 if you have the `vsnprintf' function. */ -#undef HAVE_VSNPRINTF - /* Define to 1 if you have the `vsyslog' function. */ #undef HAVE_VSYSLOG +/* Define to 1 if you have the `_wcserror_s' function. */ +#undef HAVE__WCSERROR_S + +/* define if __atomic_load_n is supported by the compiler */ +#undef HAVE___ATOMIC_LOAD_N + +/* define if __atomic_store_n is supported by the compiler */ +#undef HAVE___ATOMIC_STORE_N + /* IPv6 */ #undef INET6 -/* if unaligned access fails */ -#undef LBL_ALIGN - -/* path for device for USB sniffing */ -#undef LINUX_USB_MON_DEV - /* Define to 1 if netinet/ether.h declares `ether_hostton' */ #undef NETINET_ETHER_H_DECLARES_ETHER_HOSTTON @@ -325,23 +304,26 @@ /* support D-Bus sniffing */ #undef PCAP_SUPPORT_DBUS +/* target host supports DPDK */ +#undef PCAP_SUPPORT_DPDK + +/* target host supports Linux usbmon for USB sniffing */ +#undef PCAP_SUPPORT_LINUX_USBMON + /* target host supports netfilter sniffing */ #undef PCAP_SUPPORT_NETFILTER /* target host supports netmap */ #undef PCAP_SUPPORT_NETMAP -/* use packet ring capture support on Linux if available */ -#undef PCAP_SUPPORT_PACKET_RING - /* target host supports RDMA sniffing */ #undef PCAP_SUPPORT_RDMASNIFF -/* target host supports USB sniffing */ -#undef PCAP_SUPPORT_USB +/* The size of `const void *', as computed by sizeof. */ +#undef SIZEOF_CONST_VOID_P -/* include ACN support */ -#undef SITA +/* The size of `void *', as computed by sizeof. */ +#undef SIZEOF_VOID_P /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS diff --git a/config.sub b/config.sub index c95acc681d1b..fbaa37f2352d 100755 --- a/config.sub +++ b/config.sub @@ -1,12 +1,14 @@ #! /bin/sh # Configuration validation subroutine script. -# Copyright 1992-2018 Free Software Foundation, Inc. +# Copyright 1992-2022 Free Software Foundation, Inc. -timestamp='2018-07-03' +# shellcheck disable=SC2006,SC2268 # see below for rationale + +timestamp='2022-08-01' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or +# the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but @@ -33,7 +35,7 @@ timestamp='2018-07-03' # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: -# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub +# https://git.savannah.gnu.org/cgit/config.git/plain/config.sub # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases @@ -50,6 +52,13 @@ timestamp='2018-07-03' # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. +# The "shellcheck disable" line above the timestamp inhibits complaints +# about features and limitations of the classic Bourne shell that were +# superseded or lifted in POSIX. However, this script identifies a wide +# variety of pre-POSIX systems that do not have POSIX shells at all, and +# even some reasonably current systems (Solaris 10 as case-in-point) still +# have a pre-POSIX /bin/sh. + me=`echo "$0" | sed -e 's,.*/,,'` usage="\ @@ -67,7 +76,7 @@ Report bugs and patches to ." version="\ GNU config.sub ($timestamp) -Copyright 1992-2018 Free Software Foundation, Inc. +Copyright 1992-2022 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -89,7 +98,7 @@ while test $# -gt 0 ; do - ) # Use stdin as input. break ;; -* ) - echo "$me: invalid option $1$help" + echo "$me: invalid option $1$help" >&2 exit 1 ;; *local*) @@ -111,9 +120,12 @@ case $# in esac # Split fields of configuration type -IFS="-" read -r field1 field2 field3 field4 <&2 - exit 1 + cpu=$basic_machine + vendor=unknown + ;; +esac + +unset -v basic_machine + +# Decode basic machines in the full and proper CPU-Company form. +case $cpu-$vendor in + # Here we handle the default manufacturer of certain CPU types in canonical form. It is in + # some cases the only manufacturer, in others, it is the most popular. + craynv-unknown) + vendor=cray + basic_os=${basic_os:-unicosmp} + ;; + c90-unknown | c90-cray) + vendor=cray + basic_os=${Basic_os:-unicos} + ;; + fx80-unknown) + vendor=alliant + ;; + romp-unknown) + vendor=ibm + ;; + mmix-unknown) + vendor=knuth + ;; + microblaze-unknown | microblazeel-unknown) + vendor=xilinx + ;; + rs6000-unknown) + vendor=ibm + ;; + vax-unknown) + vendor=dec + ;; + pdp11-unknown) + vendor=dec + ;; + we32k-unknown) + vendor=att + ;; + cydra-unknown) + vendor=cydrome + ;; + i370-ibm*) + vendor=ibm + ;; + orion-unknown) + vendor=highlevel + ;; + xps-unknown | xps100-unknown) + cpu=xps100 + vendor=honeywell + ;; + + # Here we normalize CPU types with a missing or matching vendor + armh-unknown | armh-alt) + cpu=armv7l + vendor=alt + basic_os=${basic_os:-linux-gnueabihf} + ;; + dpx20-unknown | dpx20-bull) + cpu=rs6000 + vendor=bull + basic_os=${basic_os:-bosx} + ;; + + # Here we normalize CPU types irrespective of the vendor + amd64-*) + cpu=x86_64 + ;; + blackfin-*) + cpu=bfin + basic_os=linux + ;; + c54x-*) + cpu=tic54x + ;; + c55x-*) + cpu=tic55x + ;; + c6x-*) + cpu=tic6x + ;; + e500v[12]-*) + cpu=powerpc + basic_os=${basic_os}"spe" + ;; + mips3*-*) + cpu=mips64 + ;; + ms1-*) + cpu=mt + ;; + m68knommu-*) + cpu=m68k + basic_os=linux + ;; + m9s12z-* | m68hcs12z-* | hcs12z-* | s12z-*) + cpu=s12z + ;; + openrisc-*) + cpu=or32 + ;; + parisc-*) + cpu=hppa + basic_os=linux + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + cpu=i586 + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-* | athalon_*-*) + cpu=i686 + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + cpu=i686 + ;; + pentium4-*) + cpu=i786 + ;; + pc98-*) + cpu=i386 + ;; + ppc-* | ppcbe-*) + cpu=powerpc + ;; + ppcle-* | powerpclittle-*) + cpu=powerpcle + ;; + ppc64-*) + cpu=powerpc64 + ;; + ppc64le-* | powerpc64little-*) + cpu=powerpc64le + ;; + sb1-*) + cpu=mipsisa64sb1 + ;; + sb1el-*) + cpu=mipsisa64sb1el + ;; + sh5e[lb]-*) + cpu=`echo "$cpu" | sed 's/^\(sh.\)e\(.\)$/\1\2e/'` + ;; + spur-*) + cpu=spur + ;; + strongarm-* | thumb-*) + cpu=arm + ;; + tx39-*) + cpu=mipstx39 + ;; + tx39el-*) + cpu=mipstx39el + ;; + x64-*) + cpu=x86_64 + ;; + xscale-* | xscalee[bl]-*) + cpu=`echo "$cpu" | sed 's/^xscale/arm/'` + ;; + arm64-* | aarch64le-*) + cpu=aarch64 + ;; + + # Recognize the canonical CPU Types that limit and/or modify the + # company names they are paired with. + cr16-*) + basic_os=${basic_os:-elf} + ;; + crisv32-* | etraxfs*-*) + cpu=crisv32 + vendor=axis + ;; + cris-* | etrax*-*) + cpu=cris + vendor=axis + ;; + crx-*) + basic_os=${basic_os:-elf} + ;; + neo-tandem) + cpu=neo + vendor=tandem + ;; + nse-tandem) + cpu=nse + vendor=tandem + ;; + nsr-tandem) + cpu=nsr + vendor=tandem + ;; + nsv-tandem) + cpu=nsv + vendor=tandem + ;; + nsx-tandem) + cpu=nsx + vendor=tandem + ;; + mipsallegrexel-sony) + cpu=mipsallegrexel + vendor=sony + ;; + tile*-*) + basic_os=${basic_os:-linux-gnu} + ;; + + *) + # Recognize the canonical CPU types that are allowed with any + # company name. + case $cpu in + 1750a | 580 \ + | a29k \ + | aarch64 | aarch64_be \ + | abacus \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] \ + | alphapca5[67] | alpha64pca5[67] \ + | am33_2.0 \ + | amdgcn \ + | arc | arceb | arc32 | arc64 \ + | arm | arm[lb]e | arme[lb] | armv* \ + | avr | avr32 \ + | asmjs \ + | ba \ + | be32 | be64 \ + | bfin | bpf | bs2000 \ + | c[123]* | c30 | [cjt]90 | c4x \ + | c8051 | clipper | craynv | csky | cydra \ + | d10v | d30v | dlx | dsp16xx \ + | e2k | elxsi | epiphany \ + | f30[01] | f700 | fido | fr30 | frv | ft32 | fx80 \ + | h8300 | h8500 \ + | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | hexagon \ + | i370 | i*86 | i860 | i960 | ia16 | ia64 \ + | ip2k | iq2000 \ + | k1om \ + | le32 | le64 \ + | lm32 \ + | loongarch32 | loongarch64 \ + | m32c | m32r | m32rle \ + | m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | m68k \ + | m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \ + | m88110 | m88k | maxq | mb | mcore | mep | metag \ + | microblaze | microblazeel \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64eb | mips64el \ + | mips64octeon | mips64octeonel \ + | mips64orion | mips64orionel \ + | mips64r5900 | mips64r5900el \ + | mips64vr | mips64vrel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mips64vr5900 | mips64vr5900el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa32r3 | mipsisa32r3el \ + | mipsisa32r5 | mipsisa32r5el \ + | mipsisa32r6 | mipsisa32r6el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64r3 | mipsisa64r3el \ + | mipsisa64r5 | mipsisa64r5el \ + | mipsisa64r6 | mipsisa64r6el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipsr5900 | mipsr5900el \ + | mipstx39 | mipstx39el \ + | mmix \ + | mn10200 | mn10300 \ + | moxie \ + | mt \ + | msp430 \ + | nds32 | nds32le | nds32be \ + | nfp \ + | nios | nios2 | nios2eb | nios2el \ + | none | np1 | ns16k | ns32k | nvptx \ + | open8 \ + | or1k* \ + | or32 \ + | orion \ + | picochip \ + | pdp10 | pdp11 | pj | pjl | pn | power \ + | powerpc | powerpc64 | powerpc64le | powerpcle | powerpcspe \ + | pru \ + | pyramid \ + | riscv | riscv32 | riscv32be | riscv64 | riscv64be \ + | rl78 | romp | rs6000 | rx \ + | s390 | s390x \ + | score \ + | sh | shl \ + | sh[1234] | sh[24]a | sh[24]ae[lb] | sh[23]e | she[lb] | sh[lb]e \ + | sh[1234]e[lb] | sh[12345][lb]e | sh[23]ele | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet \ + | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v | sv1 | sx* \ + | spu \ + | tahoe \ + | thumbv7* \ + | tic30 | tic4x | tic54x | tic55x | tic6x | tic80 \ + | tron \ + | ubicom32 \ + | v70 | v850 | v850e | v850e1 | v850es | v850e2 | v850e2v3 \ + | vax \ + | visium \ + | w65 \ + | wasm32 | wasm64 \ + | we32k \ + | x86 | x86_64 | xc16x | xgate | xps100 \ + | xstormy16 | xtensa* \ + | ymp \ + | z8k | z80) + ;; + + *) + echo Invalid configuration \`"$1"\': machine \`"$cpu-$vendor"\' not recognized 1>&2 + exit 1 + ;; + esac ;; esac # Here we canonicalize certain aliases for manufacturers. -case $basic_machine in - *-digital*) - basic_machine=`echo "$basic_machine" | sed 's/digital.*/dec/'` +case $vendor in + digital*) + vendor=dec ;; - *-commodore*) - basic_machine=`echo "$basic_machine" | sed 's/commodore.*/cbm/'` + commodore*) + vendor=cbm ;; *) ;; @@ -1286,8 +1306,49 @@ esac # Decode manufacturer-specific aliases for certain operating systems. -if [ x$os != x ] +if test x$basic_os != x then + +# First recognize some ad-hoc cases, or perhaps split kernel-os, or else just +# set os. +case $basic_os in + gnu/linux*) + kernel=linux + os=`echo "$basic_os" | sed -e 's|gnu/linux|gnu|'` + ;; + os2-emx) + kernel=os2 + os=`echo "$basic_os" | sed -e 's|os2-emx|emx|'` + ;; + nto-qnx*) + kernel=nto + os=`echo "$basic_os" | sed -e 's|nto-qnx|qnx|'` + ;; + *-*) + # shellcheck disable=SC2162 + saved_IFS=$IFS + IFS="-" read kernel os <&2 - exit 1 + # No normalization, but not necessarily accepted, that comes below. ;; esac + else # Here we handle the default operating systems that come with various machines. @@ -1541,7 +1527,8 @@ else # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. -case $basic_machine in +kernel= +case $cpu-$vendor in score-*) os=elf ;; @@ -1552,7 +1539,8 @@ case $basic_machine in os=riscix1.2 ;; arm*-rebel) - os=linux + kernel=linux + os=gnu ;; arm*-semi) os=aout @@ -1718,86 +1706,180 @@ case $basic_machine in os=none ;; esac + fi -# Here we handle the case where we know the os, and the CPU type, but not the -# manufacturer. We pick the logical manufacturer. -vendor=unknown -case $basic_machine in - *-unknown) - case $os in - riscix*) - vendor=acorn - ;; - sunos*) - vendor=sun - ;; - cnk*|-aix*) - vendor=ibm - ;; - beos*) - vendor=be - ;; - hpux*) - vendor=hp - ;; - mpeix*) - vendor=hp - ;; - hiux*) - vendor=hitachi - ;; - unos*) - vendor=crds - ;; - dgux*) - vendor=dg - ;; - luna*) - vendor=omron - ;; - genix*) - vendor=ns - ;; - clix*) - vendor=intergraph - ;; - mvs* | opened*) - vendor=ibm - ;; - os400*) - vendor=ibm - ;; - ptx*) - vendor=sequent - ;; - tpf*) - vendor=ibm - ;; - vxsim* | vxworks* | windiss*) - vendor=wrs - ;; - aux*) - vendor=apple - ;; - hms*) - vendor=hitachi - ;; - mpw* | macos*) - vendor=apple - ;; - *mint | mint[0-9]* | *MiNT | MiNT[0-9]*) - vendor=atari - ;; - vos*) - vendor=stratus - ;; - esac - basic_machine=`echo "$basic_machine" | sed "s/unknown/$vendor/"` +# Now, validate our (potentially fixed-up) OS. +case $os in + # Sometimes we do "kernel-libc", so those need to count as OSes. + musl* | newlib* | relibc* | uclibc*) + ;; + # Likewise for "kernel-abi" + eabi* | gnueabi*) + ;; + # VxWorks passes extra cpu info in the 4th filed. + simlinux | simwindows | spe) + ;; + # Now accept the basic system types. + # The portable systems comes first. + # Each alternative MUST end in a * to match a version number. + gnu* | android* | bsd* | mach* | minix* | genix* | ultrix* | irix* \ + | *vms* | esix* | aix* | cnk* | sunos | sunos[34]* \ + | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ + | sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \ + | hiux* | abug | nacl* | netware* | windows* \ + | os9* | macos* | osx* | ios* \ + | mpw* | magic* | mmixware* | mon960* | lnews* \ + | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ + | aos* | aros* | cloudabi* | sortix* | twizzler* \ + | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \ + | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \ + | mirbsd* | netbsd* | dicos* | openedition* | ose* \ + | bitrig* | openbsd* | secbsd* | solidbsd* | libertybsd* | os108* \ + | ekkobsd* | freebsd* | riscix* | lynxos* | os400* \ + | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \ + | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \ + | udi* | lites* | ieee* | go32* | aux* | hcos* \ + | chorusrdb* | cegcc* | glidix* | serenity* \ + | cygwin* | msys* | pe* | moss* | proelf* | rtems* \ + | midipix* | mingw32* | mingw64* | mint* \ + | uxpv* | beos* | mpeix* | udk* | moxiebox* \ + | interix* | uwin* | mks* | rhapsody* | darwin* \ + | openstep* | oskit* | conix* | pw32* | nonstopux* \ + | storm-chaos* | tops10* | tenex* | tops20* | its* \ + | os2* | vos* | palmos* | uclinux* | nucleus* | morphos* \ + | scout* | superux* | sysv* | rtmk* | tpf* | windiss* \ + | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \ + | skyos* | haiku* | rdos* | toppers* | drops* | es* \ + | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \ + | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \ + | nsk* | powerunix* | genode* | zvmoe* | qnx* | emx* | zephyr* \ + | fiwix* ) + ;; + # This one is extra strict with allowed versions + sco3.2v2 | sco3.2v[4-9]* | sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + ;; + none) + ;; + *) + echo Invalid configuration \`"$1"\': OS \`"$os"\' not recognized 1>&2 + exit 1 ;; esac -echo "$basic_machine-$os" +# As a final step for OS-related things, validate the OS-kernel combination +# (given a valid OS), if there is a kernel. +case $kernel-$os in + linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* \ + | linux-musl* | linux-relibc* | linux-uclibc* ) + ;; + uclinux-uclibc* ) + ;; + -dietlibc* | -newlib* | -musl* | -relibc* | -uclibc* ) + # These are just libc implementations, not actual OSes, and thus + # require a kernel. + echo "Invalid configuration \`$1': libc \`$os' needs explicit kernel." 1>&2 + exit 1 + ;; + kfreebsd*-gnu* | kopensolaris*-gnu*) + ;; + vxworks-simlinux | vxworks-simwindows | vxworks-spe) + ;; + nto-qnx*) + ;; + os2-emx) + ;; + *-eabi* | *-gnueabi*) + ;; + -*) + # Blank kernel with real OS is always fine. + ;; + *-*) + echo "Invalid configuration \`$1': Kernel \`$kernel' not known to work with OS \`$os'." 1>&2 + exit 1 + ;; +esac + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +case $vendor in + unknown) + case $cpu-$os in + *-riscix*) + vendor=acorn + ;; + *-sunos*) + vendor=sun + ;; + *-cnk* | *-aix*) + vendor=ibm + ;; + *-beos*) + vendor=be + ;; + *-hpux*) + vendor=hp + ;; + *-mpeix*) + vendor=hp + ;; + *-hiux*) + vendor=hitachi + ;; + *-unos*) + vendor=crds + ;; + *-dgux*) + vendor=dg + ;; + *-luna*) + vendor=omron + ;; + *-genix*) + vendor=ns + ;; + *-clix*) + vendor=intergraph + ;; + *-mvs* | *-opened*) + vendor=ibm + ;; + *-os400*) + vendor=ibm + ;; + s390-* | s390x-*) + vendor=ibm + ;; + *-ptx*) + vendor=sequent + ;; + *-tpf*) + vendor=ibm + ;; + *-vxsim* | *-vxworks* | *-windiss*) + vendor=wrs + ;; + *-aux*) + vendor=apple + ;; + *-hms*) + vendor=hitachi + ;; + *-mpw* | *-macos*) + vendor=apple + ;; + *-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*) + vendor=atari + ;; + *-vos*) + vendor=stratus + ;; + esac + ;; +esac + +echo "$cpu-$vendor-${kernel:+$kernel-}$os" exit # Local variables: diff --git a/configure b/configure index fa15fc7314eb..91fd1806e8a4 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for pcap 1.9.1. +# Generated by GNU Autoconf 2.69 for pcap 1.10.3. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -577,8 +577,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='pcap' PACKAGE_TARNAME='pcap' -PACKAGE_VERSION='1.9.1' -PACKAGE_STRING='pcap 1.9.1' +PACKAGE_VERSION='1.10.3' +PACKAGE_STRING='pcap 1.10.3' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -620,65 +620,74 @@ ac_includes_default="\ #endif" ac_subst_vars='LTLIBOBJS -INSTALL_DATA -INSTALL_SCRIPT -INSTALL_PROGRAM -RDMA_SRC -PCAP_SUPPORT_RDMASNIFF -DBUS_SRC -PCAP_SUPPORT_DBUS -PKGCONFIG -BT_MONITOR_SRC -BT_SRC -PCAP_SUPPORT_BT -NETMAP_SRC -PCAP_SUPPORT_NETMAP -NETFILTER_SRC -PCAP_SUPPORT_NETFILTER -USB_SRC -PCAP_SUPPORT_USB -EXTRA_NETWORK_LIBS RPCAPD_LIBS INSTALL_RPCAPD BUILD_RPCAPD PTHREAD_LIBS -MAN_ADMIN_COMMANDS -MAN_MISC_INFO -MAN_FILE_FORMATS -MAN_DEVICES -DYEXT -SSRC +REMOTE_C_SRC +MODULE_C_SRC +PLATFORM_CXX_SRC +PLATFORM_C_SRC ADDLARCHIVEOBJS ADDLOBJS -V_YACC -V_RPATH_OPT +RPATH V_SONAME_OPT V_SHLIB_OPT V_SHLIB_CMD V_SHLIB_CCOPT -V_PCAP -V_LEX -V_INCLS -V_FINDALLDEVS -V_DEFS -V_PROG_LDFLAGS_FAT -V_PROG_CCOPT_FAT -V_LIB_LDFLAGS_FAT -V_LIB_CCOPT_FAT -V_CCOPT +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +PCAP_SUPPORT_RDMASNIFF +LIBIBVERBS_LIBS_STATIC +LIBIBVERBS_LIBS +LIBIBVERBS_CFLAGS +PCAP_SUPPORT_DBUS +DBUS_LIBS_STATIC +DBUS_LIBS +DBUS_CFLAGS +PCAP_SUPPORT_BT +PCAP_SUPPORT_DPDK +DPDK_LIBS_STATIC +DPDK_LIBS +DPDK_CFLAGS +PCAP_SUPPORT_NETMAP +PCAP_SUPPORT_NETFILTER +PCAP_SUPPORT_LINUX_USBMON MKDEP DEPENDENCY_CFLAG LN_S AR RANLIB -YFLAGS -YACC +MAN_ADMIN_COMMANDS +MAN_MISC_INFO +MAN_FILE_FORMATS +MAN_DEVICES +DYEXT +V_PROG_LDFLAGS_FAT +V_PROG_CCOPT_FAT +V_LIB_LDFLAGS_FAT +V_LIB_CCOPT_FAT +REENTRANT_PARSER +BISON_BYACC LEXLIB LEX_OUTPUT_ROOT LEX -PCAP_SUPPORT_PACKET_RING +OPENSSL_LIBS_STATIC +OPENSSL_LIBS +OPENSSL_CFLAGS +LIBNL_LIBS_STATIC +LIBNL_LIBS +LIBNL_CFLAGS +BREW +PKG_CONFIG_LIBDIR +PKG_CONFIG_PATH +PKG_CONFIG VALGRINDTEST_SRC LIBOBJS +ac_ct_CXX +CXXFLAGS +CXX EGREP GREP CPP @@ -702,6 +711,12 @@ build_os build_vendor build_cpu build +LIBS_PRIVATE +REQUIRES_PRIVATE +LIBS_STATIC +V_INCLS +V_DEFS +V_CCOPT target_alias host_alias build_alias @@ -721,7 +736,6 @@ infodir docdir oldincludedir includedir -runstatedir localstatedir sharedstatedir sysconfdir @@ -747,10 +761,8 @@ enable_option_checking with_gcc enable_largefile enable_protochain -with_sita with_pcap with_libnl -enable_packet_ring enable_ipv6 with_dag with_dag_includes @@ -767,6 +779,7 @@ enable_universal enable_shared enable_usb enable_netmap +with_dpdk enable_bluetooth enable_dbus enable_rdma @@ -780,8 +793,27 @@ LDFLAGS LIBS CPPFLAGS CPP -YACC -YFLAGS' +CXX +CXXFLAGS +CCC +PKG_CONFIG +PKG_CONFIG_PATH +PKG_CONFIG_LIBDIR +LIBNL_CFLAGS +LIBNL_LIBS +LIBNL_LIBS_STATIC +OPENSSL_CFLAGS +OPENSSL_LIBS +OPENSSL_LIBS_STATIC +DPDK_CFLAGS +DPDK_LIBS +DPDK_LIBS_STATIC +DBUS_CFLAGS +DBUS_LIBS +DBUS_LIBS_STATIC +LIBIBVERBS_CFLAGS +LIBIBVERBS_LIBS +LIBIBVERBS_LIBS_STATIC' # Initialize some variables set by options. @@ -820,7 +852,6 @@ datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' -runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' @@ -1073,15 +1104,6 @@ do | -silent | --silent | --silen | --sile | --sil) silent=yes ;; - -runstatedir | --runstatedir | --runstatedi | --runstated \ - | --runstate | --runstat | --runsta | --runst | --runs \ - | --run | --ru | --r) - ac_prev=runstatedir ;; - -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ - | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ - | --run=* | --ru=* | --r=*) - runstatedir=$ac_optarg ;; - -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ @@ -1219,7 +1241,7 @@ fi for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir runstatedir + libdir localedir mandir do eval ac_val=\$$ac_var # Remove trailing slashes. @@ -1332,7 +1354,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures pcap 1.9.1 to adapt to many kinds of systems. +\`configure' configures pcap 1.10.3 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1372,7 +1394,6 @@ Fine tuning of the installation directories: --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] - --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] @@ -1399,7 +1420,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of pcap 1.9.1:";; + short | recursive ) echo "Configuration of pcap 1.10.3:";; esac cat <<\_ACEOF @@ -1409,17 +1430,15 @@ Optional Features: --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --disable-largefile omit support for large files --disable-protochain disable \"protochain\" insn - --enable-packet-ring enable packet ring support on Linux [default=yes] --enable-ipv6 build IPv6-capable version [default=yes] --enable-remote enable remote packet capture [default=no] - --disable-remote disable remote packet capture --enable-optimizer-dbg build optimizer debugging code --enable-yydebug build parser debugging code --disable-universal don't build universal on macOS --enable-shared build shared libraries [default=yes, if support available] - --enable-usb enable USB capture support [default=yes, if support - available] + --enable-usb enable Linux usbmon USB capture support + [default=yes, if support available] --enable-netmap enable netmap support [default=yes, if support available] --enable-bluetooth enable Bluetooth support [default=yes, if support @@ -1433,7 +1452,6 @@ Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --without-gcc don't use gcc - --with-sita include SITA support --with-pcap=TYPE use packet capture TYPE --without-libnl disable libnl support [default=yes, on Linux, if present] @@ -1454,6 +1472,8 @@ Optional Packages: --with-turbocap[=DIR] include Riverbed TurboCap support (located in directory DIR, if supplied). [default=yes, if present] + --with-dpdk[=DIR] include DPDK support (located in directory DIR, if + supplied). [default=yes, if present] Some influential environment variables: CC C compiler command @@ -1464,12 +1484,39 @@ Some influential environment variables: CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor - YACC The `Yet Another Compiler Compiler' implementation to use. - Defaults to the first program found out of: `bison -y', `byacc', - `yacc'. - YFLAGS The list of arguments that will be passed by default to $YACC. - This script will default YFLAGS to the empty string to avoid a - default value of `-d' given by some make applications. + CXX C++ compiler command + CXXFLAGS C++ compiler flags + PKG_CONFIG path to pkg-config utility + PKG_CONFIG_PATH + directories to add to pkg-config's search path + PKG_CONFIG_LIBDIR + path overriding pkg-config's built-in search path + LIBNL_CFLAGS + C compiler flags for libnl-genl-3.0, overriding pkg-config + LIBNL_LIBS linker flags for libnl-genl-3.0, overriding pkg-config + LIBNL_LIBS_STATIC + static-link linker flags for libnl-genl-3.0, overriding + pkg-config + OPENSSL_CFLAGS + C compiler flags for openssl, overriding pkg-config + OPENSSL_LIBS + linker flags for openssl, overriding pkg-config + OPENSSL_LIBS_STATIC + static-link linker flags for openssl, overriding pkg-config + DPDK_CFLAGS C compiler flags for libdpdk, overriding pkg-config + DPDK_LIBS linker flags for libdpdk, overriding pkg-config + DPDK_LIBS_STATIC + static-link linker flags for libdpdk, overriding pkg-config + DBUS_CFLAGS C compiler flags for dbus-1, overriding pkg-config + DBUS_LIBS linker flags for dbus-1, overriding pkg-config + DBUS_LIBS_STATIC + static-link linker flags for dbus-1, overriding pkg-config + LIBIBVERBS_CFLAGS + C compiler flags for libibverbs, overriding pkg-config + LIBIBVERBS_LIBS + linker flags for libibverbs, overriding pkg-config + LIBIBVERBS_LIBS_STATIC + static-link linker flags for libibverbs, overriding pkg-config Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. @@ -1537,7 +1584,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -pcap configure 1.9.1 +pcap configure 1.10.3 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1589,13 +1636,13 @@ fi } # ac_fn_c_try_compile -# ac_fn_c_try_link LINENO -# ----------------------- -# Try to link conftest.$ac_ext, and return whether this succeeded. -ac_fn_c_try_link () +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; @@ -1603,37 +1650,216 @@ case "(($ac_try" in esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>conftest.err + (eval "$ac_link") 2>&5 ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && { - test "$cross_compiling" = yes || - test -x conftest$ac_exeext - }; then : + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : ac_retval=0 else - $as_echo "$as_me: failed program was:" >&5 + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 - ac_retval=1 + ac_retval=$ac_status fi - # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information - # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would - # interfere with the next link command; also delete a directory that is - # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval -} # ac_fn_c_try_link +} # ac_fn_c_try_run + +# ac_fn_c_compute_int LINENO EXPR VAR INCLUDES +# -------------------------------------------- +# Tries to find the compile-time value of EXPR in a program that includes +# INCLUDES, setting VAR accordingly. Returns whether the value could be +# computed +ac_fn_c_compute_int () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) >= 0)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_lo=0 ac_mid=0 + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) <= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_hi=$ac_mid; break +else + as_fn_arith $ac_mid + 1 && ac_lo=$as_val + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) < 0)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_hi=-1 ac_mid=-1 + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) >= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_lo=$ac_mid; break +else + as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + ac_lo= ac_hi= +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) <= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_hi=$ac_mid +else + as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in #(( +?*) eval "$3=\$ac_lo"; ac_retval=0 ;; +'') ac_retval=1 ;; +esac + else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +static long int longval () { return $2; } +static unsigned long int ulongval () { return $2; } +#include +#include +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + return 1; + if (($2) < 0) + { + long int i = longval (); + if (i != ($2)) + return 1; + fprintf (f, "%ld", i); + } + else + { + unsigned long int i = ulongval (); + if (i != ($2)) + return 1; + fprintf (f, "%lu", i); + } + /* Do not output a trailing newline, as this causes \r\n confusion + on some platforms. */ + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + echo >>conftest.val; read $3 &5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_compile + +# ac_fn_cxx_try_compile LINENO +# ---------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_compile + +# ac_fn_cxx_try_run LINENO +# ------------------------ +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_cxx_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_run + +# ac_fn_cxx_compute_int LINENO EXPR VAR INCLUDES +# ---------------------------------------------- +# Tries to find the compile-time value of EXPR in a program that includes +# INCLUDES, setting VAR accordingly. Returns whether the value could be +# computed +ac_fn_cxx_compute_int () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) >= 0)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_lo=0 ac_mid=0 + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) <= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_hi=$ac_mid; break +else + as_fn_arith $ac_mid + 1 && ac_lo=$as_val + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) < 0)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_hi=-1 ac_mid=-1 + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) >= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_lo=$ac_mid; break +else + as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + ac_lo= ac_hi= +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) <= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_hi=$ac_mid +else + as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in #(( +?*) eval "$3=\$ac_lo"; ac_retval=0 ;; +'') ac_retval=1 ;; +esac + else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +static long int longval () { return $2; } +static unsigned long int ulongval () { return $2; } +#include +#include +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + return 1; + if (($2) < 0) + { + long int i = longval (); + if (i != ($2)) + return 1; + fprintf (f, "%ld", i); + } + else + { + unsigned long int i = ulongval (); + if (i != ($2)) + return 1; + fprintf (f, "%lu", i); + } + /* Do not output a trailing newline, as this causes \r\n confusion + on some platforms. */ + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_run "$LINENO"; then : + echo >>conftest.val; read $3 &5 - (eval "$ac_link") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' - { { case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; }; then : - ac_retval=0 -else - $as_echo "$as_me: program exited with status $ac_status" >&5 - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=$ac_status -fi - rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_run - -# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES -# ------------------------------------------------------- -# Tests whether HEADER exists and can be compiled using the include files in -# INCLUDES, setting the cache variable VAR accordingly. -ac_fn_c_check_header_compile () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -#include <$2> -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - eval "$3=yes" -else - eval "$3=no" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_header_compile - # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly @@ -2059,7 +2506,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by pcap $as_me 1.9.1, which was +It was created by pcap $as_me 1.10.3, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2410,6 +2857,86 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu +# +# These are the variables that are used in Makefile, pcap-config, and +# libpcap.pc. +# +# CFLAGS: inherited from the environment, not modified by us (except +# temporarily during tests that involve compilation). Used only when +# compiling C source. +# +# CXXFLAGS: inherited from the environment, not modified by us. Used only +# when compiling C++ source. +# +# LDFLAGS: inherited from the environment, not modified by us. +# +# LIBS: inherited from the environment; we add libraries required by +# libpcap. Librares that the core libpcap code requires are added +# first; libraries required by additional pcap modules are first +# added to ADDITIONAL_LIBS, and only added to LIBS at the end, after +# we're finished doing configuration tests for the modules. +# +# LIBS_STATIC: libraries with which a program using the libpcap *static* +# library needs to be linked. This is a superset of LIBS, used in +# pcap-config, so that "pcap-config --libs --static" will report them. +# Initialized to LIBS. +# +# REQUIRES_PRIVATE: pkg-config package names for additional libraries +# with which a program using the libpcap *static* library needs to be +# linked and for which a .pc file exists. This is used in libpcap.pc, +# so that "pkg-config --libs --static" will report them, and so that +# those libraries will be determined using the library's .pc file, not +# from our .pc file. Initialized to an empty string. +# +# V_CCOPT: additional compiler flags other than -I and -D flags +# needed when compiling libpcap. Used in Makefile for both C and +# C++ source. +# +# V_DEFS: additional -D compiler flags needed when compiling +# libpcap. Used in Makefile for both C and C++ source. +# +# V_INCLS: additional -I compiler flags needed when compiling +# libpcap. Used in Makefile for both C and C++ source. +# +# ADDITIONAL_LIBS: additional libraries with which the libpcap dynamic +# library needs to be linked. Used in Makwfile; not used in pcap-config +# or libpcap.pc, as, in all platforms on which we run, if a dynamic +# library is linked with other dynamic libraries, a program using +# that dynamic library doesn't have to link with those libraries - +# they will be automatically loaded at run time. Initialized to an +# empty string. +# +# ADDITIONAL_LIBS_STATIC: additional libraries with which a program +# using the libpcap *static* library needs to be linked. This is used +# in pcap-config, so that "pcap-config --libs --static" will report +# them. Initialized to an empty string. +# +# REQUIRES_PRIVATE: pkg-config package names for additional libraries +# with which a program using the libpcap *static* library needs to be +# linked and for which a .pc file exists. This is used in libpcap.pc, +# so that "pkg-config --libs --static" will report them, and so that +# those libraries will be determined using the library's .pc file, not +# from our .pc file. Initialized to an empty string. +# +# LIBS_PRIVATE: pkg-config package names for additional libraries with +# which a program using the libpcap *static* library needs to be linked +# and for which a .pc file does not exist. This is used in libpcap.pc, +# so that "pkg-config --libs --static" will report them (those libraries +# cannot be determined using the library's .pc file, as there is no such +# file, so it has to come from our .pc file. Initialized to an empty +# string. +# +LIBS_STATIC="" +REQUIRES_PRIVATE="" +LIBS_PRIVATE="" + + + + + + + + ac_aux_dir= for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do if test -f "$ac_dir/install-sh"; then @@ -2624,8 +3151,9 @@ fi fi # -# Try to enable as many C99 features as we can. -# At minimum, we want C++/C99-style // comments. +# We require C99 or later. +# Try to get it, which may involve adding compiler flags; +# if that fails, give up. # ac_ext=c ac_cpp='$CPP $CPPFLAGS' @@ -3594,890 +4122,13 @@ fi if test "$ac_cv_prog_cc_c99" = "no"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: The C compiler does not support C99; there may be compiler errors" >&5 -$as_echo "$as_me: WARNING: The C compiler does not support C99; there may be compiler errors" >&2;} + as_fn_error $? "The C compiler does not support C99" "$LINENO" 5 fi - - - - - if test "$GCC" = yes ; then - # - # -Werror forces warnings to be errors. - # - ac_lbl_cc_force_warning_errors=-Werror - - # - # Try to have the compiler default to hiding symbols, - # so that only symbols explicitly exported with - # PCAP_API will be visible outside (shared) libraries. - # - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports the -fvisibility=hidden option" >&5 -$as_echo_n "checking whether the compiler supports the -fvisibility=hidden option... " >&6; } - save_CFLAGS="$CFLAGS" - if expr "x-fvisibility=hidden" : "x-W.*" >/dev/null - then - CFLAGS="$CFLAGS $ac_lbl_unknown_warning_option_error -fvisibility=hidden" - elif expr "x-fvisibility=hidden" : "x-f.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -fvisibility=hidden" - elif expr "x-fvisibility=hidden" : "x-m.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -fvisibility=hidden" - else - CFLAGS="$CFLAGS -fvisibility=hidden" - fi - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ -return 0 - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - can_add_to_cflags=yes - # - # The compile supports this; do we have some C code for - # which the warning should *not* appear? - # We test the fourth argument because the third argument - # could contain quotes, breaking the test. - # - if test "x" != "x" - then - CFLAGS="$CFLAGS $ac_lbl_cc_force_warning_errors" - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -fvisibility=hidden " >&5 -$as_echo_n "checking whether -fvisibility=hidden ... " >&6; } - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - - # - # Not a problem. - # - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - -else - - # - # A problem. - # - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - can_add_to_cflags=no - -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - fi - CFLAGS="$save_CFLAGS" - if test x"$can_add_to_cflags" = "xyes" - then - V_CCOPT="$V_CCOPT -fvisibility=hidden" - fi - -else - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - CFLAGS="$save_CFLAGS" - -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - - else - V_INCLS="$V_INCLS -I/usr/local/include" - LDFLAGS="$LDFLAGS -L/usr/local/lib" - - case "$host_os" in - - darwin*) - # - # This is assumed either to be GCC or clang, both - # of which use -Werror to force warnings to be errors. - # - ac_lbl_cc_force_warning_errors=-Werror - - # - # Try to have the compiler default to hiding symbols, - # so that only symbols explicitly exported with - # PCAP_API will be visible outside (shared) libraries. - # - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports the -fvisibility=hidden option" >&5 -$as_echo_n "checking whether the compiler supports the -fvisibility=hidden option... " >&6; } - save_CFLAGS="$CFLAGS" - if expr "x-fvisibility=hidden" : "x-W.*" >/dev/null - then - CFLAGS="$CFLAGS $ac_lbl_unknown_warning_option_error -fvisibility=hidden" - elif expr "x-fvisibility=hidden" : "x-f.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -fvisibility=hidden" - elif expr "x-fvisibility=hidden" : "x-m.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -fvisibility=hidden" - else - CFLAGS="$CFLAGS -fvisibility=hidden" - fi - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ -return 0 - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - can_add_to_cflags=yes - # - # The compile supports this; do we have some C code for - # which the warning should *not* appear? - # We test the fourth argument because the third argument - # could contain quotes, breaking the test. - # - if test "x" != "x" - then - CFLAGS="$CFLAGS $ac_lbl_cc_force_warning_errors" - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -fvisibility=hidden " >&5 -$as_echo_n "checking whether -fvisibility=hidden ... " >&6; } - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - - # - # Not a problem. - # - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - -else - - # - # A problem. - # - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - can_add_to_cflags=no - -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - fi - CFLAGS="$save_CFLAGS" - if test x"$can_add_to_cflags" = "xyes" - then - V_CCOPT="$V_CCOPT -fvisibility=hidden" - fi - -else - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - CFLAGS="$save_CFLAGS" - -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - - ;; - - hpux*) - # - # HP C, which is what we presume we're using, doesn't - # exit with a non-zero exit status if we hand it an - # invalid -W flag, can't be forced to do so even with - # +We, and doesn't handle GCC-style -W flags, so we - # don't want to try using GCC-style -W flags. - # - ac_lbl_cc_dont_try_gcc_dashW=yes - ;; - - irix*) - # - # MIPS C, which is what we presume we're using, doesn't - # necessarily exit with a non-zero exit status if we - # hand it an invalid -W flag, can't be forced to do - # so, and doesn't handle GCC-style -W flags, so we - # don't want to try using GCC-style -W flags. - # - ac_lbl_cc_dont_try_gcc_dashW=yes - # - # It also, apparently, defaults to "char" being - # unsigned, unlike most other C implementations; - # I suppose we could say "signed char" whenever - # we want to guarantee a signed "char", but let's - # just force signed chars. - # - # -xansi is normally the default, but the - # configure script was setting it; perhaps -cckr - # was the default in the Old Days. (Then again, - # that would probably be for backwards compatibility - # in the days when ANSI C was Shiny and New, i.e. - # 1989 and the early '90's, so maybe we can just - # drop support for those compilers.) - # - # -g is equivalent to -g2, which turns off - # optimization; we choose -g3, which generates - # debugging information but doesn't turn off - # optimization (even if the optimization would - # cause inaccuracies in debugging). - # - V_CCOPT="$V_CCOPT -xansi -signed -g3" - ;; - - osf*) - # - # Presumed to be DEC OSF/1, Digital UNIX, or - # Tru64 UNIX. - # - # The DEC C compiler, which is what we presume we're - # using, doesn't exit with a non-zero exit status if we - # hand it an invalid -W flag, can't be forced to do - # so, and doesn't handle GCC-style -W flags, so we - # don't want to try using GCC-style -W flags. - # - ac_lbl_cc_dont_try_gcc_dashW=yes - # - # -g is equivalent to -g2, which turns off - # optimization; we choose -g3, which generates - # debugging information but doesn't turn off - # optimization (even if the optimization would - # cause inaccuracies in debugging). - # - V_CCOPT="$V_CCOPT -g3" - ;; - - solaris*) - # - # Assumed to be Sun C, which requires -errwarn to force - # warnings to be treated as errors. - # - ac_lbl_cc_force_warning_errors=-errwarn - - # - # Try to have the compiler default to hiding symbols, - # so that only symbols explicitly exported with - # PCAP_API will be visible outside (shared) libraries. - # - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports the -xldscope=hidden option" >&5 -$as_echo_n "checking whether the compiler supports the -xldscope=hidden option... " >&6; } - save_CFLAGS="$CFLAGS" - if expr "x-xldscope=hidden" : "x-W.*" >/dev/null - then - CFLAGS="$CFLAGS $ac_lbl_unknown_warning_option_error -xldscope=hidden" - elif expr "x-xldscope=hidden" : "x-f.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -xldscope=hidden" - elif expr "x-xldscope=hidden" : "x-m.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -xldscope=hidden" - else - CFLAGS="$CFLAGS -xldscope=hidden" - fi - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ -return 0 - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - can_add_to_cflags=yes - # - # The compile supports this; do we have some C code for - # which the warning should *not* appear? - # We test the fourth argument because the third argument - # could contain quotes, breaking the test. - # - if test "x" != "x" - then - CFLAGS="$CFLAGS $ac_lbl_cc_force_warning_errors" - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -xldscope=hidden " >&5 -$as_echo_n "checking whether -xldscope=hidden ... " >&6; } - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - - # - # Not a problem. - # - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - -else - - # - # A problem. - # - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - can_add_to_cflags=no - -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - fi - CFLAGS="$save_CFLAGS" - if test x"$can_add_to_cflags" = "xyes" - then - V_CCOPT="$V_CCOPT -xldscope=hidden" - fi - -else - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - CFLAGS="$save_CFLAGS" - -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - - ;; - - ultrix*) - { $as_echo "$as_me:${as_lineno-$LINENO}: checking that Ultrix $CC hacks const in prototypes" >&5 -$as_echo_n "checking that Ultrix $CC hacks const in prototypes... " >&6; } - if ${ac_cv_lbl_cc_const_proto+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -int -main () -{ -struct a { int b; }; - void c(const struct a *) - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_lbl_cc_const_proto=yes -else - ac_cv_lbl_cc_const_proto=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lbl_cc_const_proto" >&5 -$as_echo "$ac_cv_lbl_cc_const_proto" >&6; } - if test $ac_cv_lbl_cc_const_proto = no ; then - -$as_echo "#define const /**/" >>confdefs.h - - fi - ;; - esac - V_CCOPT="$V_CCOPT -O" - fi - - - if test "$GCC" = yes ; then - # - # On platforms where we build a shared library: - # - # add options to generate position-independent code, - # if necessary (it's the default in AIX and Darwin/macOS); - # - # define option to set the soname of the shared library, - # if the OS supports that; - # - # add options to specify, at link time, a directory to - # add to the run-time search path, if that's necessary. - # - V_SHLIB_CMD="\$(CC)" - V_SHLIB_OPT="-shared" - case "$host_os" in - - aix*) - ;; - - freebsd*|netbsd*|openbsd*|dragonfly*|linux*|osf*|midipix*) - # - # Platforms where the linker is the GNU linker - # or accepts command-line arguments like - # those the GNU linker accepts. - # - # Some instruction sets require -fPIC on some - # operating systems. Check for them. If you - # have a combination that requires it, add it - # here. - # - PIC_OPT=-fpic - case "$host_cpu" in - - sparc64*) - case "$host_os" in - - freebsd*|openbsd*|linux*) - PIC_OPT=-fPIC - ;; - esac - ;; - esac - V_SHLIB_CCOPT="$V_SHLIB_CCOPT $PIC_OPT" - V_SONAME_OPT="-Wl,-soname," - V_RPATH_OPT="-Wl,-rpath," - ;; - - hpux*) - V_SHLIB_CCOPT="$V_SHLIB_CCOPT -fpic" - # - # XXX - this assumes GCC is using the HP linker, - # rather than the GNU linker, and that the "+h" - # option is used on all HP-UX platforms, both .sl - # and .so. - # - V_SONAME_OPT="-Wl,+h," - # - # By default, directories specifed with -L - # are added to the run-time search path, so - # we don't add them in pcap-config. - # - ;; - - solaris*) - V_SHLIB_CCOPT="$V_SHLIB_CCOPT -fpic" - # - # XXX - this assumes GCC is using the Sun linker, - # rather than the GNU linker. - # - V_SONAME_OPT="-Wl,-h," - V_RPATH_OPT="-Wl,-R," - ;; - esac - else - # - # Set the appropriate compiler flags and, on platforms - # where we build a shared library: - # - # add options to generate position-independent code, - # if necessary (it's the default in Darwin/macOS); - # - # if we generate ".so" shared libraries, define the - # appropriate options for building the shared library; - # - # add options to specify, at link time, a directory to - # add to the run-time search path, if that's necessary. - # - # Note: spaces after V_SONAME_OPT are significant; on - # some platforms the soname is passed with a GCC-like - # "-Wl,-soname,{soname}" option, with the soname part - # of the option, while on other platforms the C compiler - # driver takes it as a regular option with the soname - # following the option. The same applies to V_RPATH_OPT. - # - case "$host_os" in - - aix*) - V_SHLIB_CMD="\$(CC)" - V_SHLIB_OPT="-G -bnoentry -bexpall" - ;; - - freebsd*|netbsd*|openbsd*|dragonfly*|linux*) - # - # "cc" is GCC. - # - V_SHLIB_CCOPT="$V_SHLIB_CCOPT -fpic" - V_SHLIB_CMD="\$(CC)" - V_SHLIB_OPT="-shared" - V_SONAME_OPT="-Wl,-soname," - V_RPATH_OPT="-Wl,-rpath," - ;; - - hpux*) - V_SHLIB_CCOPT="$V_SHLIB_CCOPT +z" - V_SHLIB_CMD="\$(LD)" - V_SHLIB_OPT="-b" - V_SONAME_OPT="+h " - # - # By default, directories specifed with -L - # are added to the run-time search path, so - # we don't add them in pcap-config. - # - ;; - - osf*) - # - # Presumed to be DEC OSF/1, Digital UNIX, or - # Tru64 UNIX. - # - V_SHLIB_CMD="\$(CC)" - V_SHLIB_OPT="-shared" - V_SONAME_OPT="-soname " - V_RPATH_OPT="-rpath " - ;; - - solaris*) - V_SHLIB_CCOPT="$V_SHLIB_CCOPT -Kpic" - V_SHLIB_CMD="\$(CC)" - V_SHLIB_OPT="-G" - V_SONAME_OPT="-h " - V_RPATH_OPT="-R" - ;; - esac - fi - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 -$as_echo_n "checking for inline... " >&6; } - save_CFLAGS="$CFLAGS" - CFLAGS="$V_CCOPT" - if ${ac_cv_lbl_inline+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ac_cv_lbl_inline="" - ac_lbl_cc_inline=no - for ac_lbl_inline in inline __inline__ __inline - do - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#define inline $ac_lbl_inline - static inline struct iltest *foo(void); - struct iltest { - int iltest1; - int iltest2; - }; - - static inline struct iltest * - foo() - { - static struct iltest xxx; - - return &xxx; - } -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_lbl_cc_inline=yes -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - if test "$ac_lbl_cc_inline" = yes ; then - break; - fi - done - if test "$ac_lbl_cc_inline" = yes ; then - ac_cv_lbl_inline=$ac_lbl_inline - fi -fi - - CFLAGS="$save_CFLAGS" - if test ! -z "$ac_cv_lbl_inline" ; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lbl_inline" >&5 -$as_echo "$ac_cv_lbl_inline" >&6; } - else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - fi - -cat >>confdefs.h <<_ACEOF -#define inline $ac_cv_lbl_inline -_ACEOF - - # -# Try to arrange for large file support. +# Get the size of a void *, to determine whether this is a 32-bit +# or 64-bit build. # -# Check whether --enable-largefile was given. -if test "${enable_largefile+set}" = set; then : - enableval=$enable_largefile; -fi - -if test "$enable_largefile" != no; then - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5 -$as_echo_n "checking for special C compiler options needed for large files... " >&6; } -if ${ac_cv_sys_largefile_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_cv_sys_largefile_CC=no - if test "$GCC" != yes; then - ac_save_CC=$CC - while :; do - # IRIX 6.2 and later do not support large files by default, - # so use the C compiler's -n32 option if that helps. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - /* Check that off_t can represent 2**63 - 1 correctly. - We can't simply define LARGE_OFF_T to be 9223372036854775807, - since some C++ compilers masquerading as C compilers - incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) - int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 - && LARGE_OFF_T % 2147483647 == 1) - ? 1 : -1]; -int -main () -{ - - ; - return 0; -} -_ACEOF - if ac_fn_c_try_compile "$LINENO"; then : - break -fi -rm -f core conftest.err conftest.$ac_objext - CC="$CC -n32" - if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_sys_largefile_CC=' -n32'; break -fi -rm -f core conftest.err conftest.$ac_objext - break - done - CC=$ac_save_CC - rm -f conftest.$ac_ext - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5 -$as_echo "$ac_cv_sys_largefile_CC" >&6; } - if test "$ac_cv_sys_largefile_CC" != no; then - CC=$CC$ac_cv_sys_largefile_CC - fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5 -$as_echo_n "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; } -if ${ac_cv_sys_file_offset_bits+:} false; then : - $as_echo_n "(cached) " >&6 -else - while :; do - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - /* Check that off_t can represent 2**63 - 1 correctly. - We can't simply define LARGE_OFF_T to be 9223372036854775807, - since some C++ compilers masquerading as C compilers - incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) - int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 - && LARGE_OFF_T % 2147483647 == 1) - ? 1 : -1]; -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_sys_file_offset_bits=no; break -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#define _FILE_OFFSET_BITS 64 -#include - /* Check that off_t can represent 2**63 - 1 correctly. - We can't simply define LARGE_OFF_T to be 9223372036854775807, - since some C++ compilers masquerading as C compilers - incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) - int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 - && LARGE_OFF_T % 2147483647 == 1) - ? 1 : -1]; -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_sys_file_offset_bits=64; break -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_cv_sys_file_offset_bits=unknown - break -done -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5 -$as_echo "$ac_cv_sys_file_offset_bits" >&6; } -case $ac_cv_sys_file_offset_bits in #( - no | unknown) ;; - *) -cat >>confdefs.h <<_ACEOF -#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits -_ACEOF -;; -esac -rm -rf conftest* - if test $ac_cv_sys_file_offset_bits = unknown; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5 -$as_echo_n "checking for _LARGE_FILES value needed for large files... " >&6; } -if ${ac_cv_sys_large_files+:} false; then : - $as_echo_n "(cached) " >&6 -else - while :; do - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - /* Check that off_t can represent 2**63 - 1 correctly. - We can't simply define LARGE_OFF_T to be 9223372036854775807, - since some C++ compilers masquerading as C compilers - incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) - int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 - && LARGE_OFF_T % 2147483647 == 1) - ? 1 : -1]; -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_sys_large_files=no; break -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#define _LARGE_FILES 1 -#include - /* Check that off_t can represent 2**63 - 1 correctly. - We can't simply define LARGE_OFF_T to be 9223372036854775807, - since some C++ compilers masquerading as C compilers - incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) - int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 - && LARGE_OFF_T % 2147483647 == 1) - ? 1 : -1]; -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_sys_large_files=1; break -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_cv_sys_large_files=unknown - break -done -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5 -$as_echo "$ac_cv_sys_large_files" >&6; } -case $ac_cv_sys_large_files in #( - no | unknown) ;; - *) -cat >>confdefs.h <<_ACEOF -#define _LARGE_FILES $ac_cv_sys_large_files -_ACEOF -;; -esac -rm -rf conftest* - fi - - -fi - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGEFILE_SOURCE value needed for large files" >&5 -$as_echo_n "checking for _LARGEFILE_SOURCE value needed for large files... " >&6; } -if ${ac_cv_sys_largefile_source+:} false; then : - $as_echo_n "(cached) " >&6 -else - while :; do - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include /* for off_t */ - #include -int -main () -{ -int (*fp) (FILE *, off_t, int) = fseeko; - return fseeko (stdin, 0, 0) && fp (stdin, 0, 0); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_sys_largefile_source=no; break -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#define _LARGEFILE_SOURCE 1 -#include /* for off_t */ - #include -int -main () -{ -int (*fp) (FILE *, off_t, int) = fseeko; - return fseeko (stdin, 0, 0) && fp (stdin, 0, 0); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_sys_largefile_source=1; break -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - ac_cv_sys_largefile_source=unknown - break -done -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_source" >&5 -$as_echo "$ac_cv_sys_largefile_source" >&6; } -case $ac_cv_sys_largefile_source in #( - no | unknown) ;; - *) -cat >>confdefs.h <<_ACEOF -#define _LARGEFILE_SOURCE $ac_cv_sys_largefile_source -_ACEOF -;; -esac -rm -rf conftest* - -# We used to try defining _XOPEN_SOURCE=500 too, to work around a bug -# in glibc 2.1.3, but that breaks too many other things. -# If you want fseeko and ftello with glibc, upgrade to a fixed glibc. -if test $ac_cv_sys_largefile_source != unknown; then - -$as_echo "#define HAVE_FSEEKO 1" >>confdefs.h - -fi - ac_ext=c ac_cpp='$CPP $CPPFLAGS' @@ -4876,7 +4527,1412 @@ fi done -for ac_header in sys/ioccom.h sys/sockio.h limits.h +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of void *" >&5 +$as_echo_n "checking size of void *... " >&6; } +if ${ac_cv_sizeof_void_p+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (void *))" "ac_cv_sizeof_void_p" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_void_p" = yes; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (void *) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_void_p=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_void_p" >&5 +$as_echo "$ac_cv_sizeof_void_p" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_VOID_P $ac_cv_sizeof_void_p +_ACEOF + + +ac_lbl_c_sizeof_void_p="$ac_cv_sizeof_void_p" + +# +# We only need a C++ compiler for Haiku; all code except for its +# pcap module is in C. +# +case "$host_os" in +haiku*) + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +if test -z "$CXX"; then + if test -n "$CCC"; then + CXX=$CCC + else + if test -n "$ac_tool_prefix"; then + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CXX=$ac_cv_prog_CXX +if test -n "$CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 +$as_echo "$CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CXX" && break + done +fi +if test -z "$CXX"; then + ac_ct_CXX=$CXX + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CXX"; then + ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CXX="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CXX=$ac_cv_prog_ac_ct_CXX +if test -n "$ac_ct_CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 +$as_echo "$ac_ct_CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CXX" && break +done + + if test "x$ac_ct_CXX" = x; then + CXX="g++" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CXX=$ac_ct_CXX + fi +fi + + fi +fi +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 +$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } +if ${ac_cv_cxx_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_cxx_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 +$as_echo "$ac_cv_cxx_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GXX=yes +else + GXX= +fi +ac_test_CXXFLAGS=${CXXFLAGS+set} +ac_save_CXXFLAGS=$CXXFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 +$as_echo_n "checking whether $CXX accepts -g... " >&6; } +if ${ac_cv_prog_cxx_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_cxx_werror_flag=$ac_cxx_werror_flag + ac_cxx_werror_flag=yes + ac_cv_prog_cxx_g=no + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +else + CXXFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +else + ac_cxx_werror_flag=$ac_save_cxx_werror_flag + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cxx_werror_flag=$ac_save_cxx_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 +$as_echo "$ac_cv_prog_cxx_g" >&6; } +if test "$ac_test_CXXFLAGS" = set; then + CXXFLAGS=$ac_save_CXXFLAGS +elif test $ac_cv_prog_cxx_g = yes; then + if test "$GXX" = yes; then + CXXFLAGS="-g -O2" + else + CXXFLAGS="-g" + fi +else + if test "$GXX" = yes; then + CXXFLAGS="-O2" + else + CXXFLAGS= + fi +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + # + # Make sure C and C++ have the same pointer sizes with the flags + # they're given; if they don't, it means that the compilers for the + # languages will, with those flags, not produce code that can be + # linked together. + # + # We have to use different data types, because the results of + # a test are cached, so if we test for the size of a given type + # in C, the subsequent test in C++ will use the cached variable. + # We trick autoconf by testing the size of a "void *" in C and a + # "const void *" in C++. + # + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of const void *" >&5 +$as_echo_n "checking size of const void *... " >&6; } +if ${ac_cv_sizeof_const_void_p+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_cxx_compute_int "$LINENO" "(long int) (sizeof (const void *))" "ac_cv_sizeof_const_void_p" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_const_void_p" = yes; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (const void *) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_const_void_p=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_const_void_p" >&5 +$as_echo "$ac_cv_sizeof_const_void_p" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_CONST_VOID_P $ac_cv_sizeof_const_void_p +_ACEOF + + + ac_lbl_cxx_sizeof_void_p="$ac_cv_sizeof_const_void_p" + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + if test "$ac_lbl_cxx_sizeof_void_p" -eq 0; then + as_fn_error $? "No C++ compiler was found" "$LINENO" 5 + fi + if test "$ac_lbl_c_sizeof_void_p" -ne "$ac_lbl_cxx_sizeof_void_p"; then + as_fn_error $? "C compiler $CC produces code with $ac_lbl_c_sizeof_void_p-byte pointers +while C++ compiler $CXX produces code with $ac_lbl_cxx_sizeof_void_p-byte pointers. This prevents +code in those languages from being combined." "$LINENO" 5 + fi + ;; +esac + + + + + + if test "$GCC" = yes ; then + # + # -Werror forces warnings to be errors. + # + ac_lbl_cc_force_warning_errors=-Werror + + # + # Try to have the compiler default to hiding symbols, + # so that only symbols explicitly exported with + # PCAP_API will be visible outside (shared) libraries. + # + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports the -fvisibility=hidden option" >&5 +$as_echo_n "checking whether the compiler supports the -fvisibility=hidden option... " >&6; } + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -fvisibility=hidden" + # + # XXX - yes, this depends on the way AC_LANG_WERROR works, + # but no mechanism is provided to turn AC_LANG_WERROR on + # *and then turn it back off*, so that we *only* do it when + # testing compiler options - 15 years after somebody asked + # for it: + # + # https://autoconf.gnu.narkive.com/gTAVmfKD/how-to-cancel-flags-set-by-ac-lang-werror + # + save_ac_c_werror_flag="$ac_c_werror_flag" + ac_c_werror_flag=yes + # + # We use AC_LANG_SOURCE() so that we can control the complete + # content of the program being compiled. We do not, for example, + # want the default "int main()" that AC_LANG_PROGRAM() generates, + # as it will generate a warning with -Wold-style-definition, meaning + # that we would treat it as not working, as the test will fail if + # *any* error output, including a warning due to the flag we're + # testing, is generated; see + # + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # + # This may, as per those two messages, be fixed in autoconf 2.70, + # but we only require 2.64 or newer for now. + # + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int main(void) { return 0; } +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + can_add_to_cflags=yes + # + # The compile supports this; do we have some C code for + # which the warning should *not* appear? + # We test the fourth argument because the third argument + # could contain quotes, breaking the test. + # + if test "x" != "x" + then + CFLAGS="$CFLAGS $ac_lbl_cc_force_warning_errors" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -fvisibility=hidden " >&5 +$as_echo_n "checking whether -fvisibility=hidden ... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + # + # Not a problem. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +else + + # + # A problem. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + can_add_to_cflags=no + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi + CFLAGS="$save_CFLAGS" + if test x"$can_add_to_cflags" = "xyes" + then + V_CCOPT="$V_CCOPT -fvisibility=hidden" + fi + +else + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + CFLAGS="$save_CFLAGS" + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag="$save_ac_c_werror_flag" + + else + V_INCLS="$V_INCLS -I/usr/local/include" + LDFLAGS="$LDFLAGS -L/usr/local/lib" + + case "$host_os" in + + darwin*) + # + # This is assumed either to be GCC or clang, both + # of which use -Werror to force warnings to be errors. + # + ac_lbl_cc_force_warning_errors=-Werror + + # + # Try to have the compiler default to hiding symbols, + # so that only symbols explicitly exported with + # PCAP_API will be visible outside (shared) libraries. + # + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports the -fvisibility=hidden option" >&5 +$as_echo_n "checking whether the compiler supports the -fvisibility=hidden option... " >&6; } + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -fvisibility=hidden" + # + # XXX - yes, this depends on the way AC_LANG_WERROR works, + # but no mechanism is provided to turn AC_LANG_WERROR on + # *and then turn it back off*, so that we *only* do it when + # testing compiler options - 15 years after somebody asked + # for it: + # + # https://autoconf.gnu.narkive.com/gTAVmfKD/how-to-cancel-flags-set-by-ac-lang-werror + # + save_ac_c_werror_flag="$ac_c_werror_flag" + ac_c_werror_flag=yes + # + # We use AC_LANG_SOURCE() so that we can control the complete + # content of the program being compiled. We do not, for example, + # want the default "int main()" that AC_LANG_PROGRAM() generates, + # as it will generate a warning with -Wold-style-definition, meaning + # that we would treat it as not working, as the test will fail if + # *any* error output, including a warning due to the flag we're + # testing, is generated; see + # + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # + # This may, as per those two messages, be fixed in autoconf 2.70, + # but we only require 2.64 or newer for now. + # + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int main(void) { return 0; } +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + can_add_to_cflags=yes + # + # The compile supports this; do we have some C code for + # which the warning should *not* appear? + # We test the fourth argument because the third argument + # could contain quotes, breaking the test. + # + if test "x" != "x" + then + CFLAGS="$CFLAGS $ac_lbl_cc_force_warning_errors" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -fvisibility=hidden " >&5 +$as_echo_n "checking whether -fvisibility=hidden ... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + # + # Not a problem. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +else + + # + # A problem. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + can_add_to_cflags=no + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi + CFLAGS="$save_CFLAGS" + if test x"$can_add_to_cflags" = "xyes" + then + V_CCOPT="$V_CCOPT -fvisibility=hidden" + fi + +else + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + CFLAGS="$save_CFLAGS" + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag="$save_ac_c_werror_flag" + + ;; + + hpux*) + # + # HP C, which is what we presume we're using, doesn't + # exit with a non-zero exit status if we hand it an + # invalid -W flag, can't be forced to do so even with + # +We, and doesn't handle GCC-style -W flags, so we + # don't want to try using GCC-style -W flags. + # + ac_lbl_cc_dont_try_gcc_dashW=yes + ;; + + irix*) + # + # MIPS C, which is what we presume we're using, doesn't + # necessarily exit with a non-zero exit status if we + # hand it an invalid -W flag, can't be forced to do + # so, and doesn't handle GCC-style -W flags, so we + # don't want to try using GCC-style -W flags. + # + ac_lbl_cc_dont_try_gcc_dashW=yes + # + # It also, apparently, defaults to "char" being + # unsigned, unlike most other C implementations; + # I suppose we could say "signed char" whenever + # we want to guarantee a signed "char", but let's + # just force signed chars. + # + # -xansi is normally the default, but the + # configure script was setting it; perhaps -cckr + # was the default in the Old Days. (Then again, + # that would probably be for backwards compatibility + # in the days when ANSI C was Shiny and New, i.e. + # 1989 and the early '90's, so maybe we can just + # drop support for those compilers.) + # + # -g is equivalent to -g2, which turns off + # optimization; we choose -g3, which generates + # debugging information but doesn't turn off + # optimization (even if the optimization would + # cause inaccuracies in debugging). + # + V_CCOPT="$V_CCOPT -xansi -signed -g3" + ;; + + osf*) + # + # Presumed to be DEC OSF/1, Digital UNIX, or + # Tru64 UNIX. + # + # The DEC C compiler, which is what we presume we're + # using, doesn't exit with a non-zero exit status if we + # hand it an invalid -W flag, can't be forced to do + # so, and doesn't handle GCC-style -W flags, so we + # don't want to try using GCC-style -W flags. + # + ac_lbl_cc_dont_try_gcc_dashW=yes + # + # -g is equivalent to -g2, which turns off + # optimization; we choose -g3, which generates + # debugging information but doesn't turn off + # optimization (even if the optimization would + # cause inaccuracies in debugging). + # + V_CCOPT="$V_CCOPT -g3" + ;; + + solaris*) + # + # Assumed to be Sun C, which requires -errwarn to force + # warnings to be treated as errors. + # + ac_lbl_cc_force_warning_errors=-errwarn + + # + # Try to have the compiler default to hiding symbols, + # so that only symbols explicitly exported with + # PCAP_API will be visible outside (shared) libraries. + # + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports the -xldscope=hidden option" >&5 +$as_echo_n "checking whether the compiler supports the -xldscope=hidden option... " >&6; } + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -xldscope=hidden" + # + # XXX - yes, this depends on the way AC_LANG_WERROR works, + # but no mechanism is provided to turn AC_LANG_WERROR on + # *and then turn it back off*, so that we *only* do it when + # testing compiler options - 15 years after somebody asked + # for it: + # + # https://autoconf.gnu.narkive.com/gTAVmfKD/how-to-cancel-flags-set-by-ac-lang-werror + # + save_ac_c_werror_flag="$ac_c_werror_flag" + ac_c_werror_flag=yes + # + # We use AC_LANG_SOURCE() so that we can control the complete + # content of the program being compiled. We do not, for example, + # want the default "int main()" that AC_LANG_PROGRAM() generates, + # as it will generate a warning with -Wold-style-definition, meaning + # that we would treat it as not working, as the test will fail if + # *any* error output, including a warning due to the flag we're + # testing, is generated; see + # + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # + # This may, as per those two messages, be fixed in autoconf 2.70, + # but we only require 2.64 or newer for now. + # + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int main(void) { return 0; } +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + can_add_to_cflags=yes + # + # The compile supports this; do we have some C code for + # which the warning should *not* appear? + # We test the fourth argument because the third argument + # could contain quotes, breaking the test. + # + if test "x" != "x" + then + CFLAGS="$CFLAGS $ac_lbl_cc_force_warning_errors" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -xldscope=hidden " >&5 +$as_echo_n "checking whether -xldscope=hidden ... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + # + # Not a problem. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +else + + # + # A problem. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + can_add_to_cflags=no + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi + CFLAGS="$save_CFLAGS" + if test x"$can_add_to_cflags" = "xyes" + then + V_CCOPT="$V_CCOPT -xldscope=hidden" + fi + +else + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + CFLAGS="$save_CFLAGS" + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag="$save_ac_c_werror_flag" + + ;; + + ultrix*) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking that Ultrix $CC hacks const in prototypes" >&5 +$as_echo_n "checking that Ultrix $CC hacks const in prototypes... " >&6; } + if ${ac_cv_lbl_cc_const_proto+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +struct a { int b; }; + void c(const struct a *) + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_lbl_cc_const_proto=yes +else + ac_cv_lbl_cc_const_proto=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lbl_cc_const_proto" >&5 +$as_echo "$ac_cv_lbl_cc_const_proto" >&6; } + if test $ac_cv_lbl_cc_const_proto = no ; then + +$as_echo "#define const /**/" >>confdefs.h + + fi + ;; + esac + V_CCOPT="$V_CCOPT -O" + fi + + + if test "$GCC" = yes ; then + # + # On platforms where we build a shared library: + # + # add options to generate position-independent code, + # if necessary (it's the default in AIX and Darwin/macOS); + # + # define option to set the soname of the shared library, + # if the OS supports that; + # + # add options to specify, at link time, a directory to + # add to the run-time search path, if that's necessary. + # + V_SHLIB_CMD="\$(CC)" + V_SHLIB_OPT="-shared" + case "$host_os" in + + aix*) + ;; + + freebsd*|netbsd*|openbsd*|dragonfly*|linux*|osf*|haiku*|midipix*) + # + # Platforms where the C compiler is GCC or accepts + # compatible command-line arguments, and the linker + # is the GNU linker or accepts compatible command-line + # arguments. + # + # Some instruction sets require -fPIC on some + # operating systems. Check for them. If you + # have a combination that requires it, add it + # here. + # + PIC_OPT=-fpic + case "$host_cpu" in + + sparc64*) + case "$host_os" in + + freebsd*|openbsd*|linux*) + PIC_OPT=-fPIC + ;; + esac + ;; + esac + V_SHLIB_CCOPT="$V_SHLIB_CCOPT $PIC_OPT" + V_SONAME_OPT="-Wl,-soname," + ;; + + hpux*) + V_SHLIB_CCOPT="$V_SHLIB_CCOPT -fpic" + # + # XXX - this assumes GCC is using the HP linker, + # rather than the GNU linker, and that the "+h" + # option is used on all HP-UX platforms, both .sl + # and .so. + # + V_SONAME_OPT="-Wl,+h," + # + # By default, directories specified with -L + # are added to the run-time search path, so + # we don't add them in pcap-config. + # + ;; + + solaris*) + V_SHLIB_CCOPT="$V_SHLIB_CCOPT -fpic" + # + # Sun/Oracle's C compiler, GCC, and GCC-compatible + # compilers support -Wl,{comma-separated list of options}, + # and we use the C compiler, not ld, for all linking, + # including linking to produce a shared library. + # + V_SONAME_OPT="-Wl,-h," + ;; + esac + else + # + # Set the appropriate compiler flags and, on platforms + # where we build a shared library: + # + # add options to generate position-independent code, + # if necessary (it's the default in Darwin/macOS); + # + # if we generate ".so" shared libraries, define the + # appropriate options for building the shared library; + # + # add options to specify, at link time, a directory to + # add to the run-time search path, if that's necessary. + # + # Note: spaces after V_SONAME_OPT are significant; on + # some platforms the soname is passed with a GCC-like + # "-Wl,-soname,{soname}" option, with the soname part + # of the option, while on other platforms the C compiler + # driver takes it as a regular option with the soname + # following the option. + # + case "$host_os" in + + aix*) + V_SHLIB_CMD="\$(CC)" + V_SHLIB_OPT="-G -bnoentry -bexpall" + ;; + + freebsd*|netbsd*|openbsd*|dragonfly*|linux*) + # + # Platforms where the C compiler is GCC or accepts + # compatible command-line arguments, and the linker + # is the GNU linker or accepts compatible command-line + # arguments. + # + # XXX - does 64-bit SPARC require -fPIC? + # + V_SHLIB_CCOPT="$V_SHLIB_CCOPT -fpic" + V_SHLIB_CMD="\$(CC)" + V_SHLIB_OPT="-shared" + V_SONAME_OPT="-Wl,-soname," + ;; + + hpux*) + V_SHLIB_CCOPT="$V_SHLIB_CCOPT +z" + V_SHLIB_CMD="\$(LD)" + V_SHLIB_OPT="-b" + V_SONAME_OPT="+h " + # + # By default, directories specified with -L + # are added to the run-time search path, so + # we don't add them in pcap-config. + # + ;; + + osf*) + # + # Presumed to be DEC OSF/1, Digital UNIX, or + # Tru64 UNIX. + # + V_SHLIB_CMD="\$(CC)" + V_SHLIB_OPT="-shared" + V_SONAME_OPT="-soname " + ;; + + solaris*) + V_SHLIB_CCOPT="$V_SHLIB_CCOPT -Kpic" + V_SHLIB_CMD="\$(CC)" + V_SHLIB_OPT="-G" + # + # Sun/Oracle's C compiler, GCC, and GCC-compatible + # compilers support -Wl,{comma-separated list of options}, + # and we use the C compiler, not ld, for all linking, + # including linking to produce a shared library. + # + V_SONAME_OPT="-Wl,-h," + ;; + esac + fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 +$as_echo_n "checking for inline... " >&6; } + save_CFLAGS="$CFLAGS" + CFLAGS="$V_CCOPT" + if ${ac_cv_lbl_inline+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ac_cv_lbl_inline="" + ac_lbl_cc_inline=no + for ac_lbl_inline in inline __inline__ __inline + do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define inline $ac_lbl_inline + static inline struct iltest *foo(void); + struct iltest { + int iltest1; + int iltest2; + }; + + static inline struct iltest * + foo() + { + static struct iltest xxx; + + return &xxx; + } +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_lbl_cc_inline=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if test "$ac_lbl_cc_inline" = yes ; then + break; + fi + done + if test "$ac_lbl_cc_inline" = yes ; then + ac_cv_lbl_inline=$ac_lbl_inline + fi +fi + + CFLAGS="$save_CFLAGS" + if test ! -z "$ac_cv_lbl_inline" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lbl_inline" >&5 +$as_echo "$ac_cv_lbl_inline" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + +cat >>confdefs.h <<_ACEOF +#define inline $ac_cv_lbl_inline +_ACEOF + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __atomic_load_n" >&5 +$as_echo_n "checking for __atomic_load_n... " >&6; } + if ${ac_cv_have___atomic_load_n+:} false; then : + $as_echo_n "(cached) " >&6 +else + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + int i = 17; + int j; + j = __atomic_load_n(&i, __ATOMIC_RELAXED); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_have___atomic_load_n=yes +else + ac_have___atomic_load_n=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_have___atomic_load_n" >&5 +$as_echo "$ac_have___atomic_load_n" >&6; } + if test $ac_have___atomic_load_n = yes ; then + +$as_echo "#define HAVE___ATOMIC_LOAD_N 1" >>confdefs.h + + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __atomic_store_n" >&5 +$as_echo_n "checking for __atomic_store_n... " >&6; } + if ${ac_cv_have___atomic_store_n+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + int i; + __atomic_store_n(&i, 17, __ATOMIC_RELAXED); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_have___atomic_store_n=yes +else + ac_have___atomic_store_n=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_have___atomic_store_n" >&5 +$as_echo "$ac_have___atomic_store_n" >&6; } + if test $ac_have___atomic_store_n = yes ; then + +$as_echo "#define HAVE___ATOMIC_STORE_N 1" >>confdefs.h + + fi + +# +# Try to arrange for large file support. +# +# Check whether --enable-largefile was given. +if test "${enable_largefile+set}" = set; then : + enableval=$enable_largefile; +fi + +if test "$enable_largefile" != no; then + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5 +$as_echo_n "checking for special C compiler options needed for large files... " >&6; } +if ${ac_cv_sys_largefile_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_sys_largefile_CC=no + if test "$GCC" != yes; then + ac_save_CC=$CC + while :; do + # IRIX 6.2 and later do not support large files by default, + # so use the C compiler's -n32 option if that helps. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF + if ac_fn_c_try_compile "$LINENO"; then : + break +fi +rm -f core conftest.err conftest.$ac_objext + CC="$CC -n32" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_largefile_CC=' -n32'; break +fi +rm -f core conftest.err conftest.$ac_objext + break + done + CC=$ac_save_CC + rm -f conftest.$ac_ext + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5 +$as_echo "$ac_cv_sys_largefile_CC" >&6; } + if test "$ac_cv_sys_largefile_CC" != no; then + CC=$CC$ac_cv_sys_largefile_CC + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5 +$as_echo_n "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; } +if ${ac_cv_sys_file_offset_bits+:} false; then : + $as_echo_n "(cached) " >&6 +else + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_file_offset_bits=no; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _FILE_OFFSET_BITS 64 +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_file_offset_bits=64; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cv_sys_file_offset_bits=unknown + break +done +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5 +$as_echo "$ac_cv_sys_file_offset_bits" >&6; } +case $ac_cv_sys_file_offset_bits in #( + no | unknown) ;; + *) +cat >>confdefs.h <<_ACEOF +#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits +_ACEOF +;; +esac +rm -rf conftest* + if test $ac_cv_sys_file_offset_bits = unknown; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5 +$as_echo_n "checking for _LARGE_FILES value needed for large files... " >&6; } +if ${ac_cv_sys_large_files+:} false; then : + $as_echo_n "(cached) " >&6 +else + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_large_files=no; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _LARGE_FILES 1 +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_large_files=1; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cv_sys_large_files=unknown + break +done +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5 +$as_echo "$ac_cv_sys_large_files" >&6; } +case $ac_cv_sys_large_files in #( + no | unknown) ;; + *) +cat >>confdefs.h <<_ACEOF +#define _LARGE_FILES $ac_cv_sys_large_files +_ACEOF +;; +esac +rm -rf conftest* + fi + + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGEFILE_SOURCE value needed for large files" >&5 +$as_echo_n "checking for _LARGEFILE_SOURCE value needed for large files... " >&6; } +if ${ac_cv_sys_largefile_source+:} false; then : + $as_echo_n "(cached) " >&6 +else + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include /* for off_t */ + #include +int +main () +{ +int (*fp) (FILE *, off_t, int) = fseeko; + return fseeko (stdin, 0, 0) && fp (stdin, 0, 0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_sys_largefile_source=no; break +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _LARGEFILE_SOURCE 1 +#include /* for off_t */ + #include +int +main () +{ +int (*fp) (FILE *, off_t, int) = fseeko; + return fseeko (stdin, 0, 0) && fp (stdin, 0, 0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_sys_largefile_source=1; break +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + ac_cv_sys_largefile_source=unknown + break +done +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_source" >&5 +$as_echo "$ac_cv_sys_largefile_source" >&6; } +case $ac_cv_sys_largefile_source in #( + no | unknown) ;; + *) +cat >>confdefs.h <<_ACEOF +#define _LARGEFILE_SOURCE $ac_cv_sys_largefile_source +_ACEOF +;; +esac +rm -rf conftest* + +# We used to try defining _XOPEN_SOURCE=500 too, to work around a bug +# in glibc 2.1.3, but that breaks too many other things. +# If you want fseeko and ftello with glibc, upgrade to a fixed glibc. +if test $ac_cv_sys_largefile_source != unknown; then + +$as_echo "#define HAVE_FSEEKO 1" >>confdefs.h + +fi + + +for ac_header in sys/ioccom.h sys/sockio.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" @@ -4901,74 +5957,65 @@ fi done -for ac_header in net/pfvar.h -do : - ac_fn_c_check_header_compile "$LINENO" "net/pfvar.h" "ac_cv_header_net_pfvar_h" "#include -#include -#include -" -if test "x$ac_cv_header_net_pfvar_h" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_NET_PFVAR_H 1 -_ACEOF -fi -done + save_CFLAGS="$CFLAGS" + save_LIBS="$LIBS" + save_LDFLAGS="$LDFLAGS" -if test "$ac_cv_header_net_pfvar_h" = yes; then +case "$host_os" in +haiku*) # - # Check for various PF actions. + # Haiku needs _BSD_SOURCE for the _IO* macros because it doesn't use them. # - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether net/pfvar.h defines PF_NAT through PF_NORDR" >&5 -$as_echo_n "checking whether net/pfvar.h defines PF_NAT through PF_NORDR... " >&6; } - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + CFLAGS="$CFLAGS -D_BSD_SOURCE" + # + # Haiku has getpass in libbsd. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for getpass in -lbsd" >&5 +$as_echo_n "checking for getpass in -lbsd... " >&6; } +if ${ac_cv_lib_bsd_getpass+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lbsd $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include - #include - #include - #include + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char getpass (); int main () { -return PF_NAT+PF_NONAT+PF_BINAT+PF_NOBINAT+PF_RDR+PF_NORDR; +return getpass (); ; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - -$as_echo "#define HAVE_PF_NAT_THROUGH_PF_NORDR 1" >>confdefs.h - - +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_bsd_getpass=yes else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + ac_cv_lib_bsd_getpass=no fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS fi - -case "$host_os" in -linux*|uclinux*) - for ac_header in linux/sockios.h linux/if_bonding.h -do : - as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` -ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" " -#include -#include - -" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bsd_getpass" >&5 +$as_echo "$ac_cv_lib_bsd_getpass" >&6; } +if test "x$ac_cv_lib_bsd_getpass" = xyes; then : cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +#define HAVE_LIBBSD 1 _ACEOF -fi + LIBS="-lbsd $LIBS" -done +fi ;; esac @@ -5019,6 +6066,11 @@ $as_echo "$ac_cv_lbl_gcc_fixincludes" >&6; } fi fi + CFLAGS="$save_CFLAGS" + LIBS="$save_LIBS" + LDFLAGS="$save_LDFLAGS" + + for ac_func in strerror do : ac_fn_c_check_func "$LINENO" "strerror" "ac_cv_func_strerror" @@ -5079,14 +6131,14 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext else # - # We don't have strerror_r; do we have strerror_s? + # We don't have strerror_r; do we have _wcserror_s? # - for ac_func in strerror_s + for ac_func in _wcserror_s do : - ac_fn_c_check_func "$LINENO" "strerror_s" "ac_cv_func_strerror_s" -if test "x$ac_cv_func_strerror_s" = xyes; then : + ac_fn_c_check_func "$LINENO" "_wcserror_s" "ac_cv_func__wcserror_s" +if test "x$ac_cv_func__wcserror_s" = xyes; then : cat >>confdefs.h <<_ACEOF -#define HAVE_STRERROR_S 1 +#define HAVE__WCSERROR_S 1 _ACEOF fi @@ -5112,41 +6164,22 @@ done # -# Either: +# Make sure we have vsnprintf() and snprintf(); we require them. # -# we have snprintf() and vsnprintf(), and have asprintf() and -# vasprintf(); -# -# we have snprintf() and vsnprintf(), but don't have asprintf() -# or vasprintf(); -# -# we have neither snprintf() nor vsnprintf(), and don't have -# asprintf() or vasprintf(), either. -# -# We assume that if we have asprintf() we have vasprintf(), as well -# as snprintf() and vsnprintf(), and that if we have snprintf() we -# have vsnprintf(). -# -# For the first case, we don't need any replacement routines. -# For the second case, we need replacement asprintf()/vasprintf() -# routines. -# For the third case, we need replacement snprintf()/vsnprintf() and -# asprintf()/vasprintf() routines. -# -needsnprintf=no -for ac_func in vsnprintf snprintf -do : - as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` -ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -if eval test \"x\$"$as_ac_var"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 -_ACEOF +ac_fn_c_check_func "$LINENO" "vsnprintf" "ac_cv_func_vsnprintf" +if test "x$ac_cv_func_vsnprintf" = xyes; then : else - needsnprintf=yes + as_fn_error $? "vsnprintf() is required but wasn't found" "$LINENO" 5 fi -done + +ac_fn_c_check_func "$LINENO" "snprintf" "ac_cv_func_snprintf" +if test "x$ac_cv_func_snprintf" = xyes; then : + +else + as_fn_error $? "snprintf() is required but wasn't found" "$LINENO" 5 +fi + needasprintf=no for ac_func in vasprintf asprintf @@ -5163,23 +6196,7 @@ else fi done -if test $needsnprintf = yes; then - # - # We assume we have none of them; missing/snprintf.c supplies - # all of them. - # - case " $LIBOBJS " in - *" snprintf.$ac_objext "* ) ;; - *) LIBOBJS="$LIBOBJS snprintf.$ac_objext" - ;; -esac - -elif test $needasprintf = yes; then - # - # We assume we have snprintf()/vsnprintf() but lack - # asprintf()/vasprintf(); missing/asprintf.c supplies - # the latter (using vsnprintf()). - # +if test $needasprintf = yes; then case " $LIBOBJS " in *" asprintf.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS asprintf.$ac_objext" @@ -5356,9 +6373,61 @@ if test "x$ac_cv_lib_socket_getaddrinfo" = xyes; then : else # - # We didn't find it. + # Not found in libsocket; test for it in libnetwork, which + # is where it is in Haiku. # - as_fn_error $? "getaddrinfo is required, but wasn't found" "$LINENO" 5 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for getaddrinfo in -lnetwork" >&5 +$as_echo_n "checking for getaddrinfo in -lnetwork... " >&6; } +if ${ac_cv_lib_network_getaddrinfo+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lnetwork $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char getaddrinfo (); +int +main () +{ +return getaddrinfo (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_network_getaddrinfo=yes +else + ac_cv_lib_network_getaddrinfo=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_network_getaddrinfo" >&5 +$as_echo "$ac_cv_lib_network_getaddrinfo" >&6; } +if test "x$ac_cv_lib_network_getaddrinfo" = xyes; then : + + # + # OK, we found it in libnetwork. + # + LIBS="-lnetwork $LIBS" + +else + + # + # We didn't find it. + # + as_fn_error $? "getaddrinfo is required, but wasn't found" "$LINENO" 5 + +fi + fi @@ -5836,7 +6905,7 @@ fi # This test fails if we don't have # (if we have ether_hostton(), we should have # networking, and if we have networking, we should - # have ) or if we do but it doesn't + # have ) or if we do but it doesn't # declare ether_hostton(). # # Unset ac_cv_have_decl_ether_hostton so we don't @@ -6101,25 +7170,6 @@ $as_echo "${enable_protochain}" >&6; } # VALGRINDTEST_SRC= -# -# SITA support is mutually exclusive with native capture support; -# "--with-sita" selects SITA support. -# - -# Check whether --with-sita was given. -if test "${with_sita+set}" = set; then : - withval=$with_sita; - if test ! "x$withval" = "xno" ; then - -$as_echo "#define SITA 1" >>confdefs.h - - { $as_echo "$as_me:${as_lineno-$LINENO}: Enabling SITA ACN support" >&5 -$as_echo "$as_me: Enabling SITA ACN support" >&6;} - V_PCAP=sita - fi - -else - # Check whether --with-pcap was given. if test "${with_pcap+set}" = set; then : @@ -6252,6 +7302,18 @@ _ACEOF fi +done + + for ac_header in config/HaikuConfig.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "config/HaikuConfig.h" "ac_cv_header_config_HaikuConfig_h" "$ac_includes_default" +if test "x$ac_cv_header_config_HaikuConfig_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_CONFIG_HAIKUCONFIG_H 1 +_ACEOF + +fi + done @@ -6279,11 +7341,6 @@ done # No prizes for guessing this one. # V_PCAP=linux - - # - # XXX - this won't work with older kernels that have - # SOCK_PACKET sockets but not PF_PACKET sockets. - # VALGRINDTEST_SRC=valgrindtest.c elif test "$ac_cv_header_net_pfilt_h" = yes; then # @@ -6315,6 +7372,11 @@ done # DLPI on pre-Solaris 11 SunOS 5, HP-UX, possibly others. # V_PCAP=dlpi + elif test "$ac_cv_header_config_HaikuConfig_h" = yes; then + # + # Haiku. + # + V_PCAP=haiku else # # Nothing we support. @@ -6322,8 +7384,8 @@ done V_PCAP=null { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cannot determine packet capture interface" >&5 $as_echo "$as_me: WARNING: cannot determine packet capture interface" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: (see the INSTALL doc for more info)" >&5 -$as_echo "$as_me: WARNING: (see the INSTALL doc for more info)" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: (see the INSTALL.md file for more info)" >&5 +$as_echo "$as_me: WARNING: (see the INSTALL.md file for more info)" >&2;} fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking packet capture type" >&5 @@ -6333,15 +7395,235 @@ $as_echo "$V_PCAP" >&6; } # -# Do capture-mechanism-dependent tests. +# Do we have pkg-config? +# + + + + + + + +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. +set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_PKG_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +PKG_CONFIG=$ac_cv_path_PKG_CONFIG +if test -n "$PKG_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 +$as_echo "$PKG_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_path_PKG_CONFIG"; then + ac_pt_PKG_CONFIG=$PKG_CONFIG + # Extract the first word of "pkg-config", so it can be a program name with args. +set dummy pkg-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $ac_pt_PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG +if test -n "$ac_pt_PKG_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 +$as_echo "$ac_pt_PKG_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_pt_PKG_CONFIG" = x; then + PKG_CONFIG="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + PKG_CONFIG=$ac_pt_PKG_CONFIG + fi +else + PKG_CONFIG="$ac_cv_path_PKG_CONFIG" +fi + +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=0.9.0 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 +$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + PKG_CONFIG="" + fi +fi + +# +# Do we have the brew command from Homebrew? +# +# Extract the first word of "brew", so it can be a program name with args. +set dummy brew; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_BREW+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $BREW in + [\\/]* | ?:[\\/]*) + ac_cv_path_BREW="$BREW" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_BREW="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +BREW=$ac_cv_path_BREW +if test -n "$BREW"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $BREW" >&5 +$as_echo "$BREW" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + +# +# Solaris pkg-config is annoying. For at least one package (D-Bus, I'm +# looking at *you*!), there are separate include files for 32-bit and +# 64-bit builds (I guess using "unsigned long long" as a 64-bit integer +# type on a 64-bit build is like crossing the beams or soething), and +# there are two separate .pc files, so if we're doing a 32-bit build we +# should make sure we look in /usr/lib/pkgconfig for .pc files and if +# we're doing a 64-bit build we should make sure we look in +# /usr/lib/amd64/pkgconfig for .pc files. +# +case "$host_os" in + +solaris*) + if test "$ac_cv_sizeof_void_p" -eq 8; then + # + # 64-bit build. If the path is empty, set it to + # /usr/lib/amd64/pkgconfig; otherwise, if + # /usr/lib/pkgconfig appears in the path, prepend + # /usr/lib/amd64/pkgconfig to it; otherwise, put + # /usr/lib/amd64/pkgconfig at the end. + # + if test -z "$PKG_CONFIG_PATH"; then + # + # Not set, or empty. Set it to + # /usr/lib/amd64/pkgconfig. + # + PKG_CONFIG_PATH=/usr/lib/amd64/pkgconfig + elif test ! -z `echo "$PKG_CONFIG_PATH" | grep "/usr/lib/pkgconfig"`; then + # + # It contains /usr/lib/pkgconfig. Prepend + # /usr/lib/amd64/pkgconfig to /usr/lib/pkgconfig. + # + PKG_CONFIG_PATH=`echo "$PKG_CONFIG_PATH" | sed "s;/usr/lib/pkgconfig;/usr/lib/amd64/pkgconfig:/usr/lib/pkgconfig;"` + else + # + # Not empty, but doesn't contain /usr/lib/pkgconfig. + # Append /usr/lib/amd64/pkgconfig to it. + # + PKG_CONFIG_PATH="$PKG_CONFIG_PATH:/usr/lib/amd64/pkgconfig" + fi + export PKG_CONFIG_PATH + elif test "$ac_cv_sizeof_void_p" -eq 4; then + # + # 32-bit build. If /usr/amd64/lib/pkgconfig appears + # in the path, prepend /usr/lib/pkgconfig to it. + # + if test ! -z `echo "$PKG_CONFIG_PATH" | grep "/usr/lib/amd64/pkgconfig"`; then + # + # It contains /usr/lib/amd64/pkgconfig. Prepend + # /usr/lib/pkgconfig to /usr/lib/amd64/pkgconfig. + # + PKG_CONFIG_PATH=`echo "$PKG_CONFIG_PATH" | sed "s;/usr/lib/amd64/pkgconfig;/usr/lib/pkgconfig:/usr/lib/amd64/pkgconfig;"` + export PKG_CONFIG_PATH + fi + fi +esac + +# +# Handle each capture type. # case "$V_PCAP" in dlpi) - # - # Needed for common functions used by pcap-[dlpi,libdlpi].c - # - SSRC="dlpisubs.c" - # # Checks for some header files. # @@ -6370,7 +7652,7 @@ done # Also, due to the bug above applications that link to libpcap with # libdlpi will have to add "-L/lib" option to "configure". # - saved_ldflags=$LDFLAGS + save_LDFLAGS="$LDFLAGS" LDFLAGS="$LIBS -L/lib" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlpi_walk in -ldlpi" >&5 $as_echo_n "checking for dlpi_walk in -ldlpi... " >&6; } @@ -6411,16 +7693,32 @@ $as_echo "$ac_cv_lib_dlpi_dlpi_walk" >&6; } if test "x$ac_cv_lib_dlpi_dlpi_walk" = xyes; then : LIBS="-ldlpi $LIBS" + LIBS_STATIC="-ldlpi $LIBS_STATIC" + LIBS_PRIVATE="-ldlpi $LIBS_PRIVATE" V_PCAP=libdlpi + # + # Capture module plus common code needed for + # common functions used by pcap-[dlpi,libdlpi].c + # + PLATFORM_C_SRC="pcap-libdlpi.c dlpisubs.c" + $as_echo "#define HAVE_LIBDLPI 1" >>confdefs.h else - V_PCAP=dlpi + + V_PCAP=dlpi + + # + # Capture module plus common code needed for + # common functions used by pcap-[dlpi,libdlpi].c + # + PLATFORM_C_SRC="pcap-dlpi.c dlpisubs.c" + fi - LDFLAGS=$saved_ldflags + LDFLAGS="$save_LDFLAGS" # # Checks whether is usable, to catch weird SCO @@ -6482,7 +7780,43 @@ fi ;; +enet) + # + # Capture module + # + PLATFORM_C_SRC="pcap-enet.c" + ;; + +haiku) + # + # Capture module + # + PLATFORM_CXX_SRC="pcap-haiku.cpp" + + # + # Just for the sake of it. + # + for ac_header in net/if.h net/if_dl.h net/if_types.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + ;; + linux) + # + # Capture module + # + PLATFORM_C_SRC="pcap-linux.c" + # # Do we have the wireless extensions? # @@ -6506,6 +7840,12 @@ done # # Do we have libnl? + # We only want version 3. Version 2 was, apparently, + # short-lived, and version 1 is source and binary + # incompatible with version 3, and it appears that, + # these days, everybody's using version 3. We're + # not supporting older versions of the Linux kernel; + # let's drop support for older versions of libnl, too. # # Check whether --with-libnl was given. @@ -6517,27 +7857,172 @@ fi if test x$with_libnl != xno ; then - have_any_nl="no" - - incdir=-I/usr/include/libnl3 - libnldir= - case "$with_libnl" in - - yes|if_available) - ;; - - *) - if test -d $withval; then - libnldir=-L${withval}/lib/.libs - incdir=-I${withval}/include - fi - ;; - esac - # - # Try libnl 3.x first. + # Check for libnl-genl-3.0 with pkg-config. # - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for nl_socket_alloc in -lnl-3" >&5 + + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libnl-genl-3.0 with pkg-config" >&5 +$as_echo_n "checking for libnl-genl-3.0 with pkg-config... " >&6; } + +if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libnl-genl-3.0\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libnl-genl-3.0") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + + # + # The package was found, so try to get its C flags and + # libraries. + # + if test -n "$LIBNL_CFLAGS"; then + pkg_cv_LIBNL_CFLAGS="$LIBNL_CFLAGS" + elif test -n "$PKG_CONFIG"; then + +if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libnl-genl-3.0\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libnl-genl-3.0") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_LIBNL_CFLAGS=`$PKG_CONFIG --cflags "libnl-genl-3.0" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + if test -n "$LIBNL_LIBS"; then + pkg_cv_LIBNL_LIBS="$LIBNL_LIBS" + elif test -n "$PKG_CONFIG"; then + +if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libnl-genl-3.0\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libnl-genl-3.0") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_LIBNL_LIBS=`$PKG_CONFIG --libs "libnl-genl-3.0" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + if test -n "$LIBNL_LIBS_STATIC"; then + pkg_cv_LIBNL_LIBS_STATIC="$LIBNL_LIBS_STATIC" + elif test -n "$PKG_CONFIG"; then + +if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libnl-genl-3.0\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libnl-genl-3.0") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_LIBNL_LIBS_STATIC=`$PKG_CONFIG --libs --static "libnl-genl-3.0" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + + if test $pkg_failed = yes; then + # + # That failed - report an error. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: result: error" >&5 +$as_echo "error" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + LIBNL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libnl-genl-3.0" 2>&1` + else + LIBNL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libnl-genl-3.0" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$LIBNL_PKG_ERRORS" >&5 + + as_fn_error $? "Package requirements (libnl-genl-3.0) were not met: + +$LIBNL_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + + +Alternatively, you may set the environment variables LIBNL_CFLAGS +and LIBNL_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details." "$LINENO" 5 + elif test $pkg_failed = untried; then + # + # We don't have pkg-config, so it didn't work. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found (pkg-config not found)" >&5 +$as_echo "not found (pkg-config not found)" >&6; } + else + # + # We found the package. + # + LIBNL_CFLAGS=$pkg_cv_LIBNL_CFLAGS + LIBNL_LIBS=$pkg_cv_LIBNL_LIBS + LIBNL_LIBS_STATIC=$pkg_cv_LIBNL_LIBS_STATIC + { $as_echo "$as_me:${as_lineno-$LINENO}: result: found" >&5 +$as_echo "found" >&6; } + + pkg_config_found_libnl=yes + V_INCLS="$V_INCLS $LIBNL_CFLAGS" + ADDITIONAL_LIBS="$LIBNL_LIBS $ADDITIONAL_LIBS" + ADDITIONAL_LIBS_STATIC="$LIBNL_LIBS_STATIC $ADDITIONAL_LIBS_STATIC" + REQUIRES_PRIVATE="libnl-genl-3.0 $REQUIRES_PRIVATE" + +$as_echo "#define HAVE_LIBNL 1" >>confdefs.h + + + fi +else + + # + # The package isn't present. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5 +$as_echo "not found" >&6; } + +fi + + + if test x$pkg_config_found_libnl != xyes; then + # + # OK, either we don't have pkg-config or there + # wasn't a .pc file for it; Check for it directly. + # + case "$with_libnl" in + + yes|if_available) + incdir=-I/usr/include/libnl3 + libnldir= + ;; + + *) + if test -d $withval; then + libnldir=-L${withval}/lib + incdir=-I${withval}/include + fi + ;; + esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for nl_socket_alloc in -lnl-3" >&5 $as_echo_n "checking for nl_socket_alloc in -lnl-3... " >&6; } if ${ac_cv_lib_nl_3_nl_socket_alloc+:} false; then : $as_echo_n "(cached) " >&6 @@ -6575,195 +8060,33 @@ fi $as_echo "$ac_cv_lib_nl_3_nl_socket_alloc" >&6; } if test "x$ac_cv_lib_nl_3_nl_socket_alloc" = xyes; then : - # - # Yes, we have libnl 3.x. - # - LIBS="${libnldir} -lnl-genl-3 -lnl-3 $LIBS" + # + # Yes, we have libnl 3.x. + # + ADDITIONAL_LIBS="${libnldir} -lnl-genl-3 -lnl-3 $ADDITIONAL_LIBS" + ADDITIONAL_LIBS_STATIC="${libnldir} -lnl-genl-3 -lnl-3 $ADDITIONAL_LIBS_STATIC" + LIBS_PRIVATE="${libnldir} -lnl-genl-3 -lnl-3 $LIBS_PRIVATE" $as_echo "#define HAVE_LIBNL 1" >>confdefs.h + V_INCLS="$V_INCLS ${incdir}" -$as_echo "#define HAVE_LIBNL_3_x 1" >>confdefs.h - - -$as_echo "#define HAVE_LIBNL_NLE 1" >>confdefs.h - - -$as_echo "#define HAVE_LIBNL_SOCKETS 1" >>confdefs.h - - V_INCLS="$V_INCLS ${incdir}" - have_any_nl="yes" - -fi - - - if test x$have_any_nl = xno ; then - # - # Try libnl 2.x - # - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for nl_socket_alloc in -lnl" >&5 -$as_echo_n "checking for nl_socket_alloc in -lnl... " >&6; } -if ${ac_cv_lib_nl_nl_socket_alloc+:} false; then : - $as_echo_n "(cached) " >&6 else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lnl $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char nl_socket_alloc (); -int -main () -{ -return nl_socket_alloc (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_nl_nl_socket_alloc=yes -else - ac_cv_lib_nl_nl_socket_alloc=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_nl_nl_socket_alloc" >&5 -$as_echo "$ac_cv_lib_nl_nl_socket_alloc" >&6; } -if test "x$ac_cv_lib_nl_nl_socket_alloc" = xyes; then : # - # Yes, we have libnl 2.x. + # No, we don't have libnl at all. + # Fail if the user explicitly requested + # it. # - LIBS="${libnldir} -lnl-genl -lnl $LIBS" - -$as_echo "#define HAVE_LIBNL 1" >>confdefs.h - - -$as_echo "#define HAVE_LIBNL_2_x 1" >>confdefs.h - - -$as_echo "#define HAVE_LIBNL_NLE 1" >>confdefs.h - - -$as_echo "#define HAVE_LIBNL_SOCKETS 1" >>confdefs.h - - have_any_nl="yes" + if test x$with_libnl = xyes ; then + as_fn_error $? "libnl support requested but libnl not found" "$LINENO" 5 + fi fi fi - - if test x$have_any_nl = xno ; then - # - # No, we don't; do we have libnl 1.x? - # - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for nl_handle_alloc in -lnl" >&5 -$as_echo_n "checking for nl_handle_alloc in -lnl... " >&6; } -if ${ac_cv_lib_nl_nl_handle_alloc+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lnl $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char nl_handle_alloc (); -int -main () -{ -return nl_handle_alloc (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_nl_nl_handle_alloc=yes -else - ac_cv_lib_nl_nl_handle_alloc=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_nl_nl_handle_alloc" >&5 -$as_echo "$ac_cv_lib_nl_nl_handle_alloc" >&6; } -if test "x$ac_cv_lib_nl_nl_handle_alloc" = xyes; then : - - # - # Yes. - # - LIBS="${libnldir} -lnl $LIBS" - -$as_echo "#define HAVE_LIBNL 1" >>confdefs.h - - have_any_nl="yes" - -fi - - fi - - if test x$have_any_nl = xno ; then - # - # No, we don't have libnl at all. - # - if test x$with_libnl = xyes ; then - as_fn_error $? "libnl support requested but libnl not found" "$LINENO" 5 - fi - fi fi - for ac_header in linux/ethtool.h -do : - ac_fn_c_check_header_compile "$LINENO" "linux/ethtool.h" "ac_cv_header_linux_ethtool_h" " -$ac_includes_default -#include - -" -if test "x$ac_cv_header_linux_ethtool_h" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_LINUX_ETHTOOL_H 1 -_ACEOF - -fi - -done - - - # - # Check to see if struct tpacket_stats is defined in - # . If so, then pcap-linux.c can use this - # to report proper statistics. - # - # -Scott Barron - # - ac_fn_c_check_type "$LINENO" "struct tpacket_stats" "ac_cv_type_struct_tpacket_stats" " - #include - -" -if test "x$ac_cv_type_struct_tpacket_stats" = xyes; then : - -cat >>confdefs.h <<_ACEOF -#define HAVE_STRUCT_TPACKET_STATS 1 -_ACEOF - - -fi - - # # Check to see if the tpacket_auxdata struct has a tp_vlan_tci member. # @@ -6789,6 +8112,11 @@ fi ;; bpf) + # + # Capture module + # + PLATFORM_C_SRC="pcap-bpf.c" + # # Check whether we have the *BSD-style ioctls. # @@ -6828,12 +8156,44 @@ fi ;; +pf) + # + # Capture module + # + PLATFORM_C_SRC="pcap-pf.c" + ;; + +snit) + # + # Capture module + # + PLATFORM_C_SRC="pcap-snit.c" + ;; + +snoop) + # + # Capture module + # + PLATFORM_C_SRC="pcap-snoop.c" + ;; + dag) # # --with-pcap=dag is the only way to get here, and it means # "DAG support but nothing else" # V_DEFS="$V_DEFS -DDAG_ONLY" + PLATFORM_C_SRC="pcap-dag.c" + xxx_only=yes + ;; + +dpdk) + # + # --with-pcap=dpdk is the only way to get here, and it means + # "DPDK support but nothing else" + # + V_DEFS="$V_DEFS -DDPDK_ONLY" + PLATFORM_C_SRC="pcap-dpdk.c" xxx_only=yes ;; @@ -6843,6 +8203,7 @@ septel) # "Septel support but nothing else" # V_DEFS="$V_DEFS -DSEPTEL_ONLY" + PLATFORM_C_SRC="pcap-septel.c" xxx_only=yes ;; @@ -6852,10 +8213,15 @@ snf) # "SNF support but nothing else" # V_DEFS="$V_DEFS -DSNF_ONLY" + PLATFORM_C_SRC="pcap-snf.c" xxx_only=yes ;; null) + # + # Capture module + # + PLATFORM_C_SRC="pcap-null.c" ;; *) @@ -6879,7 +8245,7 @@ if test "x$ac_cv_header_ifaddrs_h" = xyes; then : # We have the header, so we use "getifaddrs()" to # get the list of interfaces. # - V_FINDALLDEVS=fad-getad.c + PLATFORM_C_SRC="$PLATFORM_C_SRC fad-getad.c" else @@ -6948,18 +8314,15 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lbl_have_siocglifconf" >&5 $as_echo "$ac_cv_lbl_have_siocglifconf" >&6; } if test $ac_cv_lbl_have_siocglifconf = yes ; then - V_FINDALLDEVS=fad-glifc.c + PLATFORM_C_SRC="$PLATFORM_C_SRC fad-glifc.c" else - V_FINDALLDEVS=fad-gifc.c + PLATFORM_C_SRC="$PLATFORM_C_SRC fad-gifc.c" fi fi fi -fi - - case "$host_os" in linux*) for ac_header in linux/net_tstamp.h @@ -6981,21 +8344,6 @@ $as_echo "$as_me: no hardware timestamp support implemented for $host_os" >&6;} ;; esac -# Check whether --enable-packet-ring was given. -if test "${enable_packet_ring+set}" = set; then : - enableval=$enable_packet_ring; -else - enable_packet_ring=yes -fi - - -if test "x$enable_packet_ring" != "xno" ; then - -$as_echo "#define PCAP_SUPPORT_PACKET_RING 1" >>confdefs.h - - -fi - # # Check for socklen_t. # @@ -7105,10 +8453,21 @@ if test "$want_dag" != no; then if test -z "$dag_lib_dir"; then dag_lib_dir="$dag_root/lib" + # + # Handle multiarch systems. + # + if test -d "$dag_lib_dir/$host" + then + dag_lib_dir="$dag_lib_dir/$host" + fi fi - V_INCLS="$V_INCLS -I$dag_include_dir" + save_CFLAGS="$CFLAGS" + save_LIBS="$LIBS" + save_LDFLAGS="$LDFLAGS" + + CFLAGS="$CFLAGS -I$dag_include_dir" for ac_header in dagapi.h do : ac_fn_c_check_header_mongrel "$LINENO" "dagapi.h" "ac_cv_header_dagapi_h" "$ac_includes_default" @@ -7122,16 +8481,27 @@ fi done + CFLAGS="$save_CFLAGS" + LIBS="$save_LIBS" + LDFLAGS="$save_LDFLAGS" + + if test "$ac_cv_header_dagapi_h" = yes; then + V_INCLS="$V_INCLS -I$dag_include_dir" + if test $V_PCAP != dag ; then - SSRC="$SSRC pcap-dag.c" + MODULE_C_SRC="$MODULE_C_SRC pcap-dag.c" fi # Check for various DAG API functions. # Don't need to save and restore LIBS to prevent -ldag being # included if there's a found-action (arg 3). - saved_ldflags=$LDFLAGS + + save_CFLAGS="$CFLAGS" + save_LIBS="$LIBS" + save_LDFLAGS="$LDFLAGS" + LDFLAGS="-L$dag_lib_dir" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dag_attach_stream in -ldag" >&5 $as_echo_n "checking for dag_attach_stream in -ldag... " >&6; } @@ -7170,11 +8540,15 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dag_dag_attach_stream" >&5 $as_echo "$ac_cv_lib_dag_dag_attach_stream" >&6; } if test "x$ac_cv_lib_dag_dag_attach_stream" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_LIBDAG 1 -_ACEOF - LIBS="-ldag $LIBS" + # + # We assume that if we have libdag we have + # libdagconf, as they're installed at the + # same time from the same package. + # + ADDITIONAL_LIBS="-L$dag_lib_dir $ADDITIONAL_LIBS -ldag -ldagconf" + ADDITIONAL_LIBS_STATIC="-L$dag_lib_dir $ADDITIONAL_LIBS_STATIC -ldag -ldagconf" + LIBS_PRIVATE="-L$dag_lib_dir $LIBS_PRIVATE -ldag -ldagconf" else as_fn_error $? "DAG library lacks streams support" "$LINENO" 5 @@ -7309,20 +8683,27 @@ $as_echo "#define HAVE_DAG_GET_STREAM_ERF_TYPES 1" >>confdefs.h fi - LDFLAGS=$saved_ldflags + CFLAGS="$save_CFLAGS" + LIBS="$save_LIBS" + LDFLAGS="$save_LDFLAGS" + # # We assume that if we have libdag we have libdagconf, # as they're installed at the same time from the same # package. # - LIBS="$LIBS -ldag -ldagconf" - LDFLAGS="$LDFLAGS -L$dag_lib_dir" - if test "$dag_large_streams" = 1; then $as_echo "#define HAVE_DAG_LARGE_STREAMS_API 1" >>confdefs.h + + save_CFLAGS="$CFLAGS" + save_LIBS="$LIBS" + save_LDFLAGS="$LDFLAGS" + + LIBS="$LIBS -ldag -ldagconf" + LDFLAGS="$LDFLAGS -L$dag_lib_dir" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for vdag_set_device_info in -lvdag" >&5 $as_echo_n "checking for vdag_set_device_info in -lvdag... " >&6; } if ${ac_cv_lib_vdag_vdag_set_device_info+:} false; then : @@ -7365,6 +8746,11 @@ else ac_dag_have_vdag="0" fi + + CFLAGS="$save_CFLAGS" + LIBS="$save_LIBS" + LDFLAGS="$save_LDFLAGS" + if test "$ac_dag_have_vdag" = 1; then $as_echo "#define HAVE_DAG_VDAG 1" >>confdefs.h @@ -7372,7 +8758,9 @@ $as_echo "#define HAVE_DAG_VDAG 1" >>confdefs.h if test "$ac_lbl_have_pthreads" != "found"; then as_fn_error $? "DAG requires pthreads, but we didn't find them" "$LINENO" 5 fi - LIBS="$LIBS $PTHREAD_LIBS" + ADDITIONAL_LIBS="$ADDITIONAL_LIBS $PTHREAD_LIBS" + ADDITIONAL_LIBS_STATIC="$ADDITIONAL_LIBS_STATIC $PTHREAD_LIBS" + LIBS_PRIVATE="$LIBS_PRIVATE $PTHREAD_LIBS" fi fi @@ -7380,7 +8768,6 @@ $as_echo "#define HAVE_DAG_VDAG 1" >>confdefs.h $as_echo "#define HAVE_DAG_API 1" >>confdefs.h else - if test "$V_PCAP" = dag; then # User requested "dag" capture type but we couldn't # find the DAG API support. @@ -7388,10 +8775,11 @@ $as_echo "#define HAVE_DAG_API 1" >>confdefs.h fi if test "$want_dag" = yes; then - # User wanted DAG support but we couldn't find it. + # User wanted DAG support but we couldn't find it. as_fn_error $? "DAG support requested with --with-dag, but the DAG headers weren't found at $dag_include_dir: make sure the DAG support is installed, specify a different path or paths if necessary, or don't request DAG support" "$LINENO" 5 fi fi + CFLAGS="$save_CFLAGS" fi @@ -7457,7 +8845,7 @@ $as_echo "yes ($septel_include_dir)" >&6; } ADDLARCHIVEOBJS="$ADDLARCHIVEOBJS $septel_tools_dir/asciibin.o $septel_tools_dir/bit2byte.o $septel_tools_dir/confirm.o $septel_tools_dir/fmtmsg.o $septel_tools_dir/gct_unix.o $septel_tools_dir/hqueue.o $septel_tools_dir/ident.o $septel_tools_dir/mem.o $septel_tools_dir/pack.o $septel_tools_dir/parse.o $septel_tools_dir/pool.o $septel_tools_dir/sdlsig.o $septel_tools_dir/strtonum.o $septel_tools_dir/timer.o $septel_tools_dir/trace.o" if test "$V_PCAP" != septel ; then - SSRC="$SSRC pcap-septel.c" + MODULE_C_SRC="$MODULE_C_SRC pcap-septel.c" fi @@ -7474,7 +8862,7 @@ $as_echo "no" >&6; } fi if test "$want_septel" = yes; then - # User wanted Septel support but we couldn't find it. + # User wanted Septel support but we couldn't find it. as_fn_error $? "Septel support requested with --with-septel, but the Septel headers weren't found at $septel_include_dir: make sure the Septel support is installed, specify a different path or paths if necessary, or don't request Septel support" "$LINENO" 5 fi fi @@ -7558,11 +8946,22 @@ $as_echo_n "checking whether we have Myricom Sniffer API... " >&6; } if test -z "$snf_lib_dir"; then snf_lib_dir="$snf_root/lib" + # + # Handle multiarch systems. + # + if test -d "$snf_lib_dir/$host" + then + snf_lib_dir="$snf_lib_dir/$host" + fi fi if test -f "$snf_include_dir/snf.h"; then # We found a header; make sure we can link with the library - saved_ldflags=$LDFLAGS + + save_CFLAGS="$CFLAGS" + save_LIBS="$LIBS" + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -L$snf_lib_dir" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for snf_init in -lsnf" >&5 $as_echo_n "checking for snf_init in -lsnf... " >&6; } @@ -7604,7 +9003,11 @@ if test "x$ac_cv_lib_snf_snf_init" = xyes; then : ac_cv_lbl_snf_api="yes" fi - LDFLAGS="$saved_ldflags" + + CFLAGS="$save_CFLAGS" + LIBS="$save_LIBS" + LDFLAGS="$save_LDFLAGS" + if test "$ac_cv_lbl_snf_api" = no; then as_fn_error $? "SNF API cannot correctly be linked; check config.log" "$LINENO" 5 fi @@ -7615,11 +9018,12 @@ fi $as_echo "yes ($snf_root)" >&6; } V_INCLS="$V_INCLS -I$snf_include_dir" - LIBS="$LIBS -lsnf" - LDFLAGS="$LDFLAGS -L$snf_lib_dir" + ADDITIONAL_LIBS="$ADDITIONAL_LIBS -L$snf_lib_dir -lsnf" + ADDITIONAL_LIBS_STATIC="$ADDITIONAL_LIBS_STATIC -L$snf_lib_dir -lsnf" + LIBS_PRIVATE="$LIBS_PRIVATE -L$snf_lib_dir -lsnf" if test "$V_PCAP" != snf ; then - SSRC="$SSRC pcap-snf.c" + MODULE_C_SRC="$MODULE_C_SRC pcap-snf.c" fi @@ -7682,12 +9086,16 @@ if test "$want_turbocap" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether TurboCap is supported" >&5 $as_echo_n "checking whether TurboCap is supported... " >&6; } + save_CFLAGS="$CFLAGS" save_LIBS="$LIBS" + save_LDFLAGS="$LDFLAGS" + if test ! -z "$turbocap_root"; then TURBOCAP_CFLAGS="-I$turbocap_root/include" - TURBOCAP_LIBS="-L$turbocap_root/lib" + TURBOCAP_LDFLAGS="-L$turbocap_root/lib" CFLAGS="$CFLAGS $TURBOCAP_CFLAGS" + LDFLAGS="$LDFLAGS $TURBOCAP_LDFLAGS" fi cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -7712,14 +9120,20 @@ if ac_fn_c_try_compile "$LINENO"; then : fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS="$save_CFLAGS" + LIBS="$save_LIBS" + LDFLAGS="$save_LDFLAGS" + if test $ac_cv_lbl_turbocap_api = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } - SSRC="$SSRC pcap-tc.c" + MODULE_C_SRC="$MODULE_C_SRC pcap-tc.c" V_INCLS="$V_INCLS $TURBOCAP_CFLAGS" - LIBS="$LIBS $TURBOCAP_LIBS -lTcApi -lpthread -lstdc++" + ADDITIONAL_LIBS="$ADDITIONAL_LIBS $TURBOCAP_LDFLAGS -lTcApi -lpthread -lstdc++" + ADDITIONAL_LIBS_STATIC="$ADDITIONAL_LIBS_STATIC $TURBOCAP_LDFLAGS -lTcApi -lpthread -lstdc++" + LIBS_PRIVATE="$LIBS_PRIVATE $TURBOCAP_LDFLAGS -lTcApi -lpthread -lstdc++" $as_echo "#define HAVE_TC_API 1" >>confdefs.h @@ -7729,7 +9143,7 @@ $as_echo "#define HAVE_TC_API 1" >>confdefs.h $as_echo "no" >&6; } if test "$want_turbocap" = yes; then - # User wanted Turbo support but we couldn't find it. + # User wanted Turbo support but we couldn't find it. as_fn_error $? "TurboCap support requested with --with-turbocap, but the TurboCap headers weren't found: make sure the TurboCap support is installed or don't request TurboCap support" "$LINENO" 5 fi fi @@ -7869,10 +9283,350 @@ _ACEOF fi + # + # Optionally, we may want to support SSL. + # Check for OpenSSL/libressl. + # + # First, try looking for it with pkg-config, if we have it. + # + # Homebrew's pkg-config does not, by default, look for + # pkg-config files for packages it has installed. + # Furthermore, at least for OpenSSL, they appear to be + # dumped in package-specific directories whose paths are + # not only package-specific but package-version-specific. + # + # So the only way to find openssl is to get the value of + # PKG_CONFIG_PATH from "brew --env openssl" and add that + # to PKG_CONFIG_PATH. (No, we can't just assume it's under + # /usr/local; Homebrew have conveniently chosen to put it + # under /opt/homebrew on ARM.) + # + # That's the nice thing about Homebrew - it makes things easier! + # Thanks! + # + save_PKG_CONFIG_PATH="$PKG_CONFIG_PATH" + if test -n "$BREW"; then + openssl_pkgconfig_dir=`$BREW --env --plain openssl | sed -n 's/PKG_CONFIG_PATH: //p'` + PKG_CONFIG_PATH="$openssl_pkgconfig_dir:$PKG_CONFIG_PATH" + fi + + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for openssl with pkg-config" >&5 +$as_echo_n "checking for openssl with pkg-config... " >&6; } + +if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"openssl\""; } >&5 + ($PKG_CONFIG --exists --print-errors "openssl") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + + # + # The package was found, so try to get its C flags and + # libraries. + # + if test -n "$OPENSSL_CFLAGS"; then + pkg_cv_OPENSSL_CFLAGS="$OPENSSL_CFLAGS" + elif test -n "$PKG_CONFIG"; then + +if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"openssl\""; } >&5 + ($PKG_CONFIG --exists --print-errors "openssl") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_OPENSSL_CFLAGS=`$PKG_CONFIG --cflags "openssl" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + if test -n "$OPENSSL_LIBS"; then + pkg_cv_OPENSSL_LIBS="$OPENSSL_LIBS" + elif test -n "$PKG_CONFIG"; then + +if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"openssl\""; } >&5 + ($PKG_CONFIG --exists --print-errors "openssl") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_OPENSSL_LIBS=`$PKG_CONFIG --libs "openssl" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + if test -n "$OPENSSL_LIBS_STATIC"; then + pkg_cv_OPENSSL_LIBS_STATIC="$OPENSSL_LIBS_STATIC" + elif test -n "$PKG_CONFIG"; then + +if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"openssl\""; } >&5 + ($PKG_CONFIG --exists --print-errors "openssl") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_OPENSSL_LIBS_STATIC=`$PKG_CONFIG --libs --static "openssl" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + + if test $pkg_failed = yes; then + # + # That failed - report an error. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: result: error" >&5 +$as_echo "error" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + OPENSSL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "openssl" 2>&1` + else + OPENSSL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "openssl" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$OPENSSL_PKG_ERRORS" >&5 + + as_fn_error $? "Package requirements (openssl) were not met: + +$OPENSSL_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + + +Alternatively, you may set the environment variables OPENSSL_CFLAGS +and OPENSSL_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details." "$LINENO" 5 + elif test $pkg_failed = untried; then + # + # We don't have pkg-config, so it didn't work. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found (pkg-config not found)" >&5 +$as_echo "not found (pkg-config not found)" >&6; } + else + # + # We found the package. + # + OPENSSL_CFLAGS=$pkg_cv_OPENSSL_CFLAGS + OPENSSL_LIBS=$pkg_cv_OPENSSL_LIBS + OPENSSL_LIBS_STATIC=$pkg_cv_OPENSSL_LIBS_STATIC + { $as_echo "$as_me:${as_lineno-$LINENO}: result: found" >&5 +$as_echo "found" >&6; } + + # + # We found OpenSSL/libressl. + # + HAVE_OPENSSL=yes + REQUIRES_PRIVATE="$REQUIRES_PRIVATE openssl" + + fi +else + + # + # The package isn't present. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5 +$as_echo "not found" >&6; } + +fi + + PKG_CONFIG_PATH="$save_PKG_CONFIG_PATH" + + # + # If it wasn't found, and we have Homebrew installed, see + # if it's in Homebrew. + # + if test "x$HAVE_OPENSSL" != "xyes" -a -n "$BREW"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for openssl in Homebrew" >&5 +$as_echo_n "checking for openssl in Homebrew... " >&6; } + # + # The brew man page lies when it speaks of + # $BREW --prefix --installed + # outputting nothing. In Homebrew 3.3.16, + # it produces output regardless of whether + # the formula is installed or not, so we + # send the standard output and error to + # the bit bucket. + # + if $BREW --prefix --installed openssl >/dev/null 2>&1; then + # + # Yes. Get the include directory and library + # directory. (No, we can't just assume it's + # under /usr/local; Homebrew have conveniently + # chosen to put it under /opt/homebrew on ARM.) + # + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + HAVE_OPENSSL=yes + openssl_path=`$BREW --prefix openssl` + OPENSSL_CFLAGS="-I$openssl_path/include" + OPENSSL_LIBS="-L$openssl_path/lib -lssl -lcrypto" + OPENSSL_LIBS_STATIC="-L$openssl_path/lib -lssl -lcrypto" + OPENSSL_LIBS_PRIVATE="-L$openssl_path/lib -lssl -lcrypto" + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + fi + + # + # If it wasn't found, and /usr/local/include and /usr/local/lib + # exist, check if it's in /usr/local. (We check whether they + # exist because, if they don't exist, the compiler will warn + # about that and then ignore the argument, so they test + # using just the system header files and libraries.) + # + # We include the standard include file to 1) make sure that + # it's installed (if it's just a shared library for the + # benefit of existing programs, that's not useful) and 2) + # because SSL_library_init() is a library routine in some + # versions and a #defined wrapper around OPENSSL_init_ssl() + # in others. + # + if test "x$HAVE_OPENSSL" != "xyes" -a -d "/usr/local/include" -a -d "/usr/local/lib"; then + + save_CFLAGS="$CFLAGS" + save_LIBS="$LIBS" + save_LDFLAGS="$LDFLAGS" + + CFLAGS="$CFLAGS -I/usr/local/include" + LIBS="$LIBS -L/usr/local/lib -lssl -lcrypto" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we have OpenSSL/libressl in /usr/local that we can use" >&5 +$as_echo_n "checking whether we have OpenSSL/libressl in /usr/local that we can use... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include + +int +main () +{ + +SSL_library_init(); +return 0; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + HAVE_OPENSSL=yes + OPENSSL_CFLAGS="-I/usr/local/include" + OPENSSL_LIBS="-L/usr/local/lib -lssl -lcrypto" + OPENSSL_LIBS_STATIC="-L/usr/local/lib -lssl -lcrypto" + OPENSSL_LIBS_PRIVATE="-L/usr/local/lib -lssl -lcrypto" + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + + CFLAGS="$save_CFLAGS" + LIBS="$save_LIBS" + LDFLAGS="$save_LDFLAGS" + + fi + + # + # If it wasn't found, check if it's a system library. + # + # We include the standard include file to 1) make sure that + # it's installed (if it's just a shared library for the + # benefit of existing programs, that's not useful) and 2) + # because SSL_library_init() is a library routine in some + # versions and a #defined wrapper around OPENSSL_init_ssl() + # in others. + # + if test "x$HAVE_OPENSSL" != "xyes"; then + + save_CFLAGS="$CFLAGS" + save_LIBS="$LIBS" + save_LDFLAGS="$LDFLAGS" + + LIBS="$LIBS -lssl -lcrypto" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we have a system OpenSSL/libressl that we can use" >&5 +$as_echo_n "checking whether we have a system OpenSSL/libressl that we can use... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include + +int +main () +{ + +SSL_library_init(); +return 0; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + HAVE_OPENSSL=yes + OPENSSL_LIBS="-lssl -lcrypto" + OPENSSL_LIBS_STATIC="-lssl -lcrypto" + OPENSSL_LIBS_PRIVATE="-lssl -lcrypto" + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + + CFLAGS="$save_CFLAGS" + LIBS="$save_LIBS" + LDFLAGS="$save_LDFLAGS" + + fi + + # + # OK, did we find it? + # + if test "x$HAVE_OPENSSL" = "xyes"; then + +$as_echo "#define HAVE_OPENSSL 1" >>confdefs.h + + V_INCLS="$V_INCLS $OPENSSL_CFLAGS" + ADDITIONAL_LIBS="$ADDITIONAL_LIBS $OPENSSL_LIBS" + ADDITIONAL_LIBS_STATIC="$ADDITIONAL_LIBS_STATIC $OPENSSL_LIBS_STATIC" + LIBS_PRIVATE="$LIBS_PRIVATE $OPENSSL_LIBS_PRIVATE" + REQUIRES_PRIVATE="$REQUIRES_PRIVATE $OPENSSL_REQUIRES_PRIVATE" + else + { $as_echo "$as_me:${as_lineno-$LINENO}: OpenSSL not found" >&5 +$as_echo "$as_me: OpenSSL not found" >&6;} + fi + $as_echo "#define ENABLE_REMOTE /**/" >>confdefs.h - SSRC="$SSRC pcap-new.c pcap-rpcap.c rpcap-protocol.c sockutils.c" + REMOTE_C_SRC="$REMOTE_C_SRC pcap-new.c pcap-rpcap.c rpcap-protocol.c sockutils.c sslutils.c" BUILD_RPCAPD=build-rpcapd INSTALL_RPCAPD=install-rpcapd ;; @@ -8096,23 +9850,38 @@ fi $as_echo "$tcpdump_cv_capable_lex" >&6; } if test $tcpdump_cv_capable_lex = insufficient ; then as_fn_error $? "$LEX is insufficient to compile libpcap. - libpcap requires Flex 2.5.31 or later, or a compatible version of lex." "$LINENO" 5 + libpcap requires Flex 2.5.31 or later, or a compatible version of lex. + If a suitable version of Lex/Flex is available as a non-standard command + and/or not in the PATH, you can specify it using the LEX environment + variable. That said, on some systems the error can mean that Flex/Lex is + actually acceptable, but m4 is not. Likewise, if a suitable version of + m4 (such as GNU M4) is available but has not been detected, you can + specify it using the M4 environment variable." "$LINENO" 5 fi # # Look for yacc/bison/byacc. +# If it's Bison, we do not want -y, as 1) we will be using -o to cause +# the output for XXX.y to be written to XXX.c and 2) we don't want +# it to issue warnings about stuff not supported by POSIX YACC - we +# want to use that stuff, and don't care whether plain YACC supports +# it or not, we require either Bison or Berkeley YACC. # -for ac_prog in 'bison -y' byacc +BISON_BYACC="" +# +# Look for Bison. +# +for ac_prog in bison do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_YACC+:} false; then : +if ${ac_cv_prog_BISON_BYACC+:} false; then : $as_echo_n "(cached) " >&6 else - if test -n "$YACC"; then - ac_cv_prog_YACC="$YACC" # Let the user override the test. + if test -n "$BISON_BYACC"; then + ac_cv_prog_BISON_BYACC="$BISON_BYACC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH @@ -8121,7 +9890,7 @@ do test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_YACC="$ac_prog" + ac_cv_prog_BISON_BYACC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi @@ -8131,44 +9900,131 @@ IFS=$as_save_IFS fi fi -YACC=$ac_cv_prog_YACC -if test -n "$YACC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $YACC" >&5 -$as_echo "$YACC" >&6; } +BISON_BYACC=$ac_cv_prog_BISON_BYACC +if test -n "$BISON_BYACC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $BISON_BYACC" >&5 +$as_echo "$BISON_BYACC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi - test -n "$YACC" && break + test -n "$BISON_BYACC" && break done -test -n "$YACC" || YACC="yacc" + +if test x"$BISON_BYACC" != x; then + # + # We found Bison. + # + # Bison prior to 2.4(.1) doesn't support "%define api.pure", so use + # "%pure-parser". + # + bison_major_version=`$BISON_BYACC -V | sed -n 's/.* \([1-9][0-9]*\)\.[0-9][0-9.]*/\1/p'` + bison_minor_version=`$BISON_BYACC -V | sed -n 's/.* [1-9][0-9]*\.\([0-9]+\).*/\1/p'` + if test "$bison_major_version" -lt 2 -o \ + \( "$bison_major_version" -eq 2 -a "$bison_major_version" -lt 4 \) + then + REENTRANT_PARSER="%pure-parser" + else + REENTRANT_PARSER="%define api.pure" + fi +else + # + # We didn't find Bison; check for Berkeley YACC, under the + # names byacc and yacc. + # + for ac_prog in byacc yacc +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_BISON_BYACC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$BISON_BYACC"; then + ac_cv_prog_BISON_BYACC="$BISON_BYACC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_BISON_BYACC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +BISON_BYACC=$ac_cv_prog_BISON_BYACC +if test -n "$BISON_BYACC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $BISON_BYACC" >&5 +$as_echo "$BISON_BYACC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi -# -# Make sure it supports the -p flag and supports processing our -# grammar.y. -# -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for capable yacc/bison" >&5 -$as_echo_n "checking for capable yacc/bison... " >&6; } + test -n "$BISON_BYACC" && break +done + + if test x"$BISON_BYACC" != x; then + # + # Make sure this is Berkeley YACC, not AT&T YACC; + # the latter doesn't support reentrant parsers. + # Run it with "-V"; that succeeds and reports the + # version number with Berkeley YACC, but will + # (probably) fail with various vendor flavors + # of AT&T YACC. + # + # Hopefully this also eliminates any versions + # of Berkeley YACC that don't support reentrant + # parsers, if there are any. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for capable yacc" >&5 +$as_echo_n "checking for capable yacc... " >&6; } if ${tcpdump_cv_capable_yacc+:} false; then : $as_echo_n "(cached) " >&6 else - if $YACC -p pcap_ -o /dev/null $srcdir/grammar.y >/dev/null 2>&1; then - tcpdump_cv_capable_yacc=yes - else - tcpdump_cv_capable_yacc=insufficient - fi + if $BISON_BYACC -V >/dev/null 2>&1; then + tcpdump_cv_capable_yacc=yes + else + tcpdump_cv_capable_yacc=insufficient + fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcpdump_cv_capable_yacc" >&5 $as_echo "$tcpdump_cv_capable_yacc" >&6; } -if test $tcpdump_cv_capable_yacc = insufficient ; then - as_fn_error $? "$YACC is insufficient to compile libpcap. + if test $tcpdump_cv_capable_yacc = insufficient ; then + as_fn_error $? "$BISON_BYACC is insufficient to compile libpcap. libpcap requires Bison, a newer version of Berkeley YACC with support for reentrant parsers, or another YACC compatible with them." "$LINENO" 5 + fi + else + # + # OK, we found neither byacc nor yacc. + # + as_fn_error $? "Neither bison, byacc, nor yacc was found. + libpcap requires Bison, a newer version of Berkeley YACC with support + for reentrant parsers, or another YACC compatible with them." "$LINENO" 5 + fi + + # + # Berkeley YACC doesn't support "%define api.pure", so use + # "%pure-parser". + # + REENTRANT_PARSER="%pure-parser" fi + + # # Do various checks for various OSes and versions of those OSes. # @@ -8266,7 +10122,7 @@ fi V_PROG_LDFLAGS_FAT="-arch ppc -arch ppc64" ;; - darwin8.[456]|darwin.[456].*) + darwin8.[456]|darwin8.[456].*) # # Tiger, subsequent to Intel support but prior # to x86-64 support. Build libraries and @@ -8331,26 +10187,26 @@ fi V_PROG_LDFLAGS_FAT="-arch x86_64 -arch i386" ;; - darwin*) + darwin1[1-8]*) # - # Post-Snow Leopard. Build libraries for x86-64 - # and 32-bit x86, with x86-64 first, and build - # executables only for x86-64. (That's what - # Apple does.) This requires no special flags - # for programs. - # XXX - update if and when Apple drops support - # for 32-bit x86 code and if and when Apple adds - # ARM-based Macs. (You're on your own for iOS - # etc.) + # Post-Snow Leopard, pre-Catalina. Build + # libraries for x86-64 and 32-bit x86, with + # x86-64 first, and build executables only for + # x86-64. (That's what Apple does.) This + # requires no special flags for programs. # - # XXX - check whether we *can* build for - # i386 and, if not, suggest that the user - # install the /usr/include headers if they - # want to build fat. + # We check whether we *can* build for i386 and, + # if not, suggest that the user install the + # /usr/include headers if they want to build + # fat. # { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether building for 32-bit x86 is supported" >&5 $as_echo_n "checking whether building for 32-bit x86 is supported... " >&6; } - save_CFLAGS="$CFLAGS" + + save_CFLAGS="$CFLAGS" + save_LIBS="$LIBS" + save_LDFLAGS="$LDFLAGS" + CFLAGS="$CFLAGS -arch i386" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -8363,12 +10219,24 @@ return 0; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : +if ac_fn_c_try_link "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } - V_LIB_CCOPT_FAT="-arch x86_64 -arch i386" - V_LIB_LDFLAGS_FAT="-arch x86_64 -arch i386" + V_LIB_CCOPT_FAT="-arch x86_64" + V_LIB_LDFLAGS_FAT="-arch x86_64" + + # + # OpenSSL installation on macOS seems + # to install only the libs for 64-bit + # x86 - at least that's what Brew does: + # only configure 32-bit builds if we + # don't have OpenSSL. + # + if test "$HAVE_OPENSSL" != yes; then + V_LIB_CCOPT_FAT="$V_LIB_CCOPT_FAT -arch i386" + V_LIB_LDFLAGS_FAT="$V_LIB_LDFLAGS_FAT -arch i386" + fi else @@ -8400,8 +10268,97 @@ $as_echo "$as_me: WARNING: Compiling for 32-bit x86 gives an error; try installi esac fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS="$save_CFLAGS" +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + + CFLAGS="$save_CFLAGS" + LIBS="$save_LIBS" + LDFLAGS="$save_LDFLAGS" + + ;; + + darwin19*) + # + # Catalina. Build libraries and executables + # only for x86-64. (That's what Apple does; + # 32-bit x86 binaries are not supported on + # Catalina.) + # + V_LIB_CCOPT_FAT="-arch x86_64" + V_LIB_LDFLAGS_FAT="-arch x86_64" + V_PROG_CCOPT_FAT="-arch x86_64" + V_PROG_LDFLAGS_FAT="-arch x86_64" + ;; + + darwin*) + # + # Post-Catalina. Build libraries and + # executables for x86-64 and ARM64. + # (That's what Apple does, except they + # build for arm64e, which may include + # some of the pointer-checking extensions.) + # + # If we're building with libssl, make sure + # we can build fat with it (i.e., that it + # was built fat); if we can't, don't set + # the target architectures, and just + # build for the host we're on. + # + # Otherwise, just add both of them. + # + if test "$HAVE_OPENSSL" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether building fat with libssl is supported" >&5 +$as_echo_n "checking whether building fat with libssl is supported... " >&6; } + + save_CFLAGS="$CFLAGS" + save_LIBS="$LIBS" + save_LDFLAGS="$LDFLAGS" + + CFLAGS="$CFLAGS -arch x86_64 -arch arm64" + LDFLAGS="$LDFLAGS $OPENSSL_LIBS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include + +int +main () +{ + + SSL_library_init(); + return 0; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + V_LIB_CCOPT_FAT="-arch x86_64 -arch arm64" + V_LIB_LDFLAGS_FAT="-arch x86_64 -arch arm64" + V_PROG_CCOPT_FAT="-arch x86_64 -arch arm64" + V_PROG_LDFLAGS_FAT="-arch x86_64 -arch arm64" + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + + CFLAGS="$save_CFLAGS" + LIBS="$save_LIBS" + LDFLAGS="$save_LDFLAGS" + + else + V_LIB_CCOPT_FAT="-arch x86_64 -arch arm64" + V_LIB_LDFLAGS_FAT="-arch x86_64 -arch arm64" + V_PROG_CCOPT_FAT="-arch x86_64 -arch arm64" + V_PROG_LDFLAGS_FAT="-arch x86_64 -arch arm64" + fi ;; esac fi @@ -8473,23 +10430,15 @@ irix*) MAN_MISC_INFO=5 ;; -linux*|freebsd*|netbsd*|openbsd*|dragonfly*|kfreebsd*|gnu*|midipix*) +linux*|freebsd*|netbsd*|openbsd*|dragonfly*|kfreebsd*|gnu*|haiku*|midipix*) DYEXT="so" - - # - # Compiler assumed to be GCC; run-time linker may require a -R - # flag. - # - if test "$libdir" != "/usr/lib"; then - V_RFLAGS=-Wl,-R$libdir - fi ;; osf*) DYEXT="so" # - # DEC OSF/1, a/k/a Digial UNIX, a/k/a Tru64 UNIX. + # DEC OSF/1, a/k/a Digital UNIX, a/k/a Tru64 UNIX. # Use Tru64 UNIX conventions for man pages; they're the same as # the System V conventions except that they use section 8 for # administrative commands and daemons. @@ -8567,6 +10516,15 @@ $as_echo "#define HAVE_SOLARIS 1" >>confdefs.h ;; esac + + + + + + + + + # Check whether --enable-shared was given. if test "${enable_shared+set}" = set; then : enableval=$enable_shared; @@ -8782,67 +10740,39 @@ rm -f os-proto.h # if test "$ac_lbl_cc_dont_try_gcc_dashW" != yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler fails when given an unknown warning option" >&5 -$as_echo_n "checking whether the compiler fails when given an unknown warning option... " >&6; } - save_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS -Wxyzzy-this-will-never-succeed-xyzzy" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ -return 0 - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - # - # We're assuming this is clang, where - # -Werror=unknown-warning-option is the appropriate - # option to force the compiler to fail. - # - ac_lbl_unknown_warning_option_error="-Werror=unknown-warning-option" - -else - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CFLAGS="$save_CFLAGS" - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports the -W option" >&5 $as_echo_n "checking whether the compiler supports the -W option... " >&6; } save_CFLAGS="$CFLAGS" - if expr "x-W" : "x-W.*" >/dev/null - then - CFLAGS="$CFLAGS $ac_lbl_unknown_warning_option_error -W" - elif expr "x-W" : "x-f.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -W" - elif expr "x-W" : "x-m.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -W" - else - CFLAGS="$CFLAGS -W" - fi + CFLAGS="$CFLAGS -W" + # + # XXX - yes, this depends on the way AC_LANG_WERROR works, + # but no mechanism is provided to turn AC_LANG_WERROR on + # *and then turn it back off*, so that we *only* do it when + # testing compiler options - 15 years after somebody asked + # for it: + # + # https://autoconf.gnu.narkive.com/gTAVmfKD/how-to-cancel-flags-set-by-ac-lang-werror + # + save_ac_c_werror_flag="$ac_c_werror_flag" + ac_c_werror_flag=yes + # + # We use AC_LANG_SOURCE() so that we can control the complete + # content of the program being compiled. We do not, for example, + # want the default "int main()" that AC_LANG_PROGRAM() generates, + # as it will generate a warning with -Wold-style-definition, meaning + # that we would treat it as not working, as the test will fail if + # *any* error output, including a warning due to the flag we're + # testing, is generated; see + # + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # + # This may, as per those two messages, be fixed in autoconf 2.70, + # but we only require 2.64 or newer for now. + # cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - -int -main () -{ -return 0 - ; - return 0; -} +int main(void) { return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : @@ -8898,33 +10828,42 @@ $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag="$save_ac_c_werror_flag" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports the -Wall option" >&5 $as_echo_n "checking whether the compiler supports the -Wall option... " >&6; } save_CFLAGS="$CFLAGS" - if expr "x-Wall" : "x-W.*" >/dev/null - then - CFLAGS="$CFLAGS $ac_lbl_unknown_warning_option_error -Wall" - elif expr "x-Wall" : "x-f.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -Wall" - elif expr "x-Wall" : "x-m.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -Wall" - else - CFLAGS="$CFLAGS -Wall" - fi + CFLAGS="$CFLAGS -Wall" + # + # XXX - yes, this depends on the way AC_LANG_WERROR works, + # but no mechanism is provided to turn AC_LANG_WERROR on + # *and then turn it back off*, so that we *only* do it when + # testing compiler options - 15 years after somebody asked + # for it: + # + # https://autoconf.gnu.narkive.com/gTAVmfKD/how-to-cancel-flags-set-by-ac-lang-werror + # + save_ac_c_werror_flag="$ac_c_werror_flag" + ac_c_werror_flag=yes + # + # We use AC_LANG_SOURCE() so that we can control the complete + # content of the program being compiled. We do not, for example, + # want the default "int main()" that AC_LANG_PROGRAM() generates, + # as it will generate a warning with -Wold-style-definition, meaning + # that we would treat it as not working, as the test will fail if + # *any* error output, including a warning due to the flag we're + # testing, is generated; see + # + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # + # This may, as per those two messages, be fixed in autoconf 2.70, + # but we only require 2.64 or newer for now. + # cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - -int -main () -{ -return 0 - ; - return 0; -} +int main(void) { return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : @@ -8980,33 +10919,42 @@ $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag="$save_ac_c_werror_flag" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports the -Wcomma option" >&5 $as_echo_n "checking whether the compiler supports the -Wcomma option... " >&6; } save_CFLAGS="$CFLAGS" - if expr "x-Wcomma" : "x-W.*" >/dev/null - then - CFLAGS="$CFLAGS $ac_lbl_unknown_warning_option_error -Wcomma" - elif expr "x-Wcomma" : "x-f.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -Wcomma" - elif expr "x-Wcomma" : "x-m.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -Wcomma" - else - CFLAGS="$CFLAGS -Wcomma" - fi + CFLAGS="$CFLAGS -Wcomma" + # + # XXX - yes, this depends on the way AC_LANG_WERROR works, + # but no mechanism is provided to turn AC_LANG_WERROR on + # *and then turn it back off*, so that we *only* do it when + # testing compiler options - 15 years after somebody asked + # for it: + # + # https://autoconf.gnu.narkive.com/gTAVmfKD/how-to-cancel-flags-set-by-ac-lang-werror + # + save_ac_c_werror_flag="$ac_c_werror_flag" + ac_c_werror_flag=yes + # + # We use AC_LANG_SOURCE() so that we can control the complete + # content of the program being compiled. We do not, for example, + # want the default "int main()" that AC_LANG_PROGRAM() generates, + # as it will generate a warning with -Wold-style-definition, meaning + # that we would treat it as not working, as the test will fail if + # *any* error output, including a warning due to the flag we're + # testing, is generated; see + # + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # + # This may, as per those two messages, be fixed in autoconf 2.70, + # but we only require 2.64 or newer for now. + # cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - -int -main () -{ -return 0 - ; - return 0; -} +int main(void) { return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : @@ -9062,115 +11010,42 @@ $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports the -Wdeclaration-after-statement option" >&5 -$as_echo_n "checking whether the compiler supports the -Wdeclaration-after-statement option... " >&6; } - save_CFLAGS="$CFLAGS" - if expr "x-Wdeclaration-after-statement" : "x-W.*" >/dev/null - then - CFLAGS="$CFLAGS $ac_lbl_unknown_warning_option_error -Wdeclaration-after-statement" - elif expr "x-Wdeclaration-after-statement" : "x-f.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -Wdeclaration-after-statement" - elif expr "x-Wdeclaration-after-statement" : "x-m.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -Wdeclaration-after-statement" - else - CFLAGS="$CFLAGS -Wdeclaration-after-statement" - fi - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ -return 0 - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - can_add_to_cflags=yes - # - # The compile supports this; do we have some C code for - # which the warning should *not* appear? - # We test the fourth argument because the third argument - # could contain quotes, breaking the test. - # - if test "x" != "x" - then - CFLAGS="$CFLAGS $ac_lbl_cc_force_warning_errors" - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -Wdeclaration-after-statement " >&5 -$as_echo_n "checking whether -Wdeclaration-after-statement ... " >&6; } - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - - # - # Not a problem. - # - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - -else - - # - # A problem. - # - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - can_add_to_cflags=no - -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - fi - CFLAGS="$save_CFLAGS" - if test x"$can_add_to_cflags" = "xyes" - then - V_CCOPT="$V_CCOPT -Wdeclaration-after-statement" - fi - -else - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - CFLAGS="$save_CFLAGS" - -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag="$save_ac_c_werror_flag" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports the -Wdocumentation option" >&5 $as_echo_n "checking whether the compiler supports the -Wdocumentation option... " >&6; } save_CFLAGS="$CFLAGS" - if expr "x-Wdocumentation" : "x-W.*" >/dev/null - then - CFLAGS="$CFLAGS $ac_lbl_unknown_warning_option_error -Wdocumentation" - elif expr "x-Wdocumentation" : "x-f.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -Wdocumentation" - elif expr "x-Wdocumentation" : "x-m.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -Wdocumentation" - else - CFLAGS="$CFLAGS -Wdocumentation" - fi + CFLAGS="$CFLAGS -Wdocumentation" + # + # XXX - yes, this depends on the way AC_LANG_WERROR works, + # but no mechanism is provided to turn AC_LANG_WERROR on + # *and then turn it back off*, so that we *only* do it when + # testing compiler options - 15 years after somebody asked + # for it: + # + # https://autoconf.gnu.narkive.com/gTAVmfKD/how-to-cancel-flags-set-by-ac-lang-werror + # + save_ac_c_werror_flag="$ac_c_werror_flag" + ac_c_werror_flag=yes + # + # We use AC_LANG_SOURCE() so that we can control the complete + # content of the program being compiled. We do not, for example, + # want the default "int main()" that AC_LANG_PROGRAM() generates, + # as it will generate a warning with -Wold-style-definition, meaning + # that we would treat it as not working, as the test will fail if + # *any* error output, including a warning due to the flag we're + # testing, is generated; see + # + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # + # This may, as per those two messages, be fixed in autoconf 2.70, + # but we only require 2.64 or newer for now. + # cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - -int -main () -{ -return 0 - ; - return 0; -} +int main(void) { return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : @@ -9226,33 +11101,42 @@ $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag="$save_ac_c_werror_flag" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports the -Wformat-nonliteral option" >&5 $as_echo_n "checking whether the compiler supports the -Wformat-nonliteral option... " >&6; } save_CFLAGS="$CFLAGS" - if expr "x-Wformat-nonliteral" : "x-W.*" >/dev/null - then - CFLAGS="$CFLAGS $ac_lbl_unknown_warning_option_error -Wformat-nonliteral" - elif expr "x-Wformat-nonliteral" : "x-f.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -Wformat-nonliteral" - elif expr "x-Wformat-nonliteral" : "x-m.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -Wformat-nonliteral" - else - CFLAGS="$CFLAGS -Wformat-nonliteral" - fi + CFLAGS="$CFLAGS -Wformat-nonliteral" + # + # XXX - yes, this depends on the way AC_LANG_WERROR works, + # but no mechanism is provided to turn AC_LANG_WERROR on + # *and then turn it back off*, so that we *only* do it when + # testing compiler options - 15 years after somebody asked + # for it: + # + # https://autoconf.gnu.narkive.com/gTAVmfKD/how-to-cancel-flags-set-by-ac-lang-werror + # + save_ac_c_werror_flag="$ac_c_werror_flag" + ac_c_werror_flag=yes + # + # We use AC_LANG_SOURCE() so that we can control the complete + # content of the program being compiled. We do not, for example, + # want the default "int main()" that AC_LANG_PROGRAM() generates, + # as it will generate a warning with -Wold-style-definition, meaning + # that we would treat it as not working, as the test will fail if + # *any* error output, including a warning due to the flag we're + # testing, is generated; see + # + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # + # This may, as per those two messages, be fixed in autoconf 2.70, + # but we only require 2.64 or newer for now. + # cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - -int -main () -{ -return 0 - ; - return 0; -} +int main(void) { return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : @@ -9308,33 +11192,42 @@ $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag="$save_ac_c_werror_flag" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports the -Wmissing-noreturn option" >&5 $as_echo_n "checking whether the compiler supports the -Wmissing-noreturn option... " >&6; } save_CFLAGS="$CFLAGS" - if expr "x-Wmissing-noreturn" : "x-W.*" >/dev/null - then - CFLAGS="$CFLAGS $ac_lbl_unknown_warning_option_error -Wmissing-noreturn" - elif expr "x-Wmissing-noreturn" : "x-f.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -Wmissing-noreturn" - elif expr "x-Wmissing-noreturn" : "x-m.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -Wmissing-noreturn" - else - CFLAGS="$CFLAGS -Wmissing-noreturn" - fi + CFLAGS="$CFLAGS -Wmissing-noreturn" + # + # XXX - yes, this depends on the way AC_LANG_WERROR works, + # but no mechanism is provided to turn AC_LANG_WERROR on + # *and then turn it back off*, so that we *only* do it when + # testing compiler options - 15 years after somebody asked + # for it: + # + # https://autoconf.gnu.narkive.com/gTAVmfKD/how-to-cancel-flags-set-by-ac-lang-werror + # + save_ac_c_werror_flag="$ac_c_werror_flag" + ac_c_werror_flag=yes + # + # We use AC_LANG_SOURCE() so that we can control the complete + # content of the program being compiled. We do not, for example, + # want the default "int main()" that AC_LANG_PROGRAM() generates, + # as it will generate a warning with -Wold-style-definition, meaning + # that we would treat it as not working, as the test will fail if + # *any* error output, including a warning due to the flag we're + # testing, is generated; see + # + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # + # This may, as per those two messages, be fixed in autoconf 2.70, + # but we only require 2.64 or newer for now. + # cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - -int -main () -{ -return 0 - ; - return 0; -} +int main(void) { return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : @@ -9390,33 +11283,42 @@ $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag="$save_ac_c_werror_flag" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports the -Wmissing-prototypes option" >&5 $as_echo_n "checking whether the compiler supports the -Wmissing-prototypes option... " >&6; } save_CFLAGS="$CFLAGS" - if expr "x-Wmissing-prototypes" : "x-W.*" >/dev/null - then - CFLAGS="$CFLAGS $ac_lbl_unknown_warning_option_error -Wmissing-prototypes" - elif expr "x-Wmissing-prototypes" : "x-f.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -Wmissing-prototypes" - elif expr "x-Wmissing-prototypes" : "x-m.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -Wmissing-prototypes" - else - CFLAGS="$CFLAGS -Wmissing-prototypes" - fi + CFLAGS="$CFLAGS -Wmissing-prototypes" + # + # XXX - yes, this depends on the way AC_LANG_WERROR works, + # but no mechanism is provided to turn AC_LANG_WERROR on + # *and then turn it back off*, so that we *only* do it when + # testing compiler options - 15 years after somebody asked + # for it: + # + # https://autoconf.gnu.narkive.com/gTAVmfKD/how-to-cancel-flags-set-by-ac-lang-werror + # + save_ac_c_werror_flag="$ac_c_werror_flag" + ac_c_werror_flag=yes + # + # We use AC_LANG_SOURCE() so that we can control the complete + # content of the program being compiled. We do not, for example, + # want the default "int main()" that AC_LANG_PROGRAM() generates, + # as it will generate a warning with -Wold-style-definition, meaning + # that we would treat it as not working, as the test will fail if + # *any* error output, including a warning due to the flag we're + # testing, is generated; see + # + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # + # This may, as per those two messages, be fixed in autoconf 2.70, + # but we only require 2.64 or newer for now. + # cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - -int -main () -{ -return 0 - ; - return 0; -} +int main(void) { return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : @@ -9472,33 +11374,42 @@ $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag="$save_ac_c_werror_flag" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports the -Wmissing-variable-declarations option" >&5 $as_echo_n "checking whether the compiler supports the -Wmissing-variable-declarations option... " >&6; } save_CFLAGS="$CFLAGS" - if expr "x-Wmissing-variable-declarations" : "x-W.*" >/dev/null - then - CFLAGS="$CFLAGS $ac_lbl_unknown_warning_option_error -Wmissing-variable-declarations" - elif expr "x-Wmissing-variable-declarations" : "x-f.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -Wmissing-variable-declarations" - elif expr "x-Wmissing-variable-declarations" : "x-m.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -Wmissing-variable-declarations" - else - CFLAGS="$CFLAGS -Wmissing-variable-declarations" - fi + CFLAGS="$CFLAGS -Wmissing-variable-declarations" + # + # XXX - yes, this depends on the way AC_LANG_WERROR works, + # but no mechanism is provided to turn AC_LANG_WERROR on + # *and then turn it back off*, so that we *only* do it when + # testing compiler options - 15 years after somebody asked + # for it: + # + # https://autoconf.gnu.narkive.com/gTAVmfKD/how-to-cancel-flags-set-by-ac-lang-werror + # + save_ac_c_werror_flag="$ac_c_werror_flag" + ac_c_werror_flag=yes + # + # We use AC_LANG_SOURCE() so that we can control the complete + # content of the program being compiled. We do not, for example, + # want the default "int main()" that AC_LANG_PROGRAM() generates, + # as it will generate a warning with -Wold-style-definition, meaning + # that we would treat it as not working, as the test will fail if + # *any* error output, including a warning due to the flag we're + # testing, is generated; see + # + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # + # This may, as per those two messages, be fixed in autoconf 2.70, + # but we only require 2.64 or newer for now. + # cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - -int -main () -{ -return 0 - ; - return 0; -} +int main(void) { return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : @@ -9554,33 +11465,224 @@ $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag="$save_ac_c_werror_flag" + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports the -Wpointer-arith option" >&5 +$as_echo_n "checking whether the compiler supports the -Wpointer-arith option... " >&6; } + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -Wpointer-arith" + # + # XXX - yes, this depends on the way AC_LANG_WERROR works, + # but no mechanism is provided to turn AC_LANG_WERROR on + # *and then turn it back off*, so that we *only* do it when + # testing compiler options - 15 years after somebody asked + # for it: + # + # https://autoconf.gnu.narkive.com/gTAVmfKD/how-to-cancel-flags-set-by-ac-lang-werror + # + save_ac_c_werror_flag="$ac_c_werror_flag" + ac_c_werror_flag=yes + # + # We use AC_LANG_SOURCE() so that we can control the complete + # content of the program being compiled. We do not, for example, + # want the default "int main()" that AC_LANG_PROGRAM() generates, + # as it will generate a warning with -Wold-style-definition, meaning + # that we would treat it as not working, as the test will fail if + # *any* error output, including a warning due to the flag we're + # testing, is generated; see + # + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # + # This may, as per those two messages, be fixed in autoconf 2.70, + # but we only require 2.64 or newer for now. + # + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int main(void) { return 0; } +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + can_add_to_cflags=yes + # + # The compile supports this; do we have some C code for + # which the warning should *not* appear? + # We test the fourth argument because the third argument + # could contain quotes, breaking the test. + # + if test "x" != "x" + then + CFLAGS="$CFLAGS $ac_lbl_cc_force_warning_errors" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -Wpointer-arith " >&5 +$as_echo_n "checking whether -Wpointer-arith ... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + # + # Not a problem. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +else + + # + # A problem. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + can_add_to_cflags=no + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi + CFLAGS="$save_CFLAGS" + if test x"$can_add_to_cflags" = "xyes" + then + V_CCOPT="$V_CCOPT -Wpointer-arith" + fi + +else + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + CFLAGS="$save_CFLAGS" + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag="$save_ac_c_werror_flag" + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports the -Wpointer-sign option" >&5 +$as_echo_n "checking whether the compiler supports the -Wpointer-sign option... " >&6; } + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -Wpointer-sign" + # + # XXX - yes, this depends on the way AC_LANG_WERROR works, + # but no mechanism is provided to turn AC_LANG_WERROR on + # *and then turn it back off*, so that we *only* do it when + # testing compiler options - 15 years after somebody asked + # for it: + # + # https://autoconf.gnu.narkive.com/gTAVmfKD/how-to-cancel-flags-set-by-ac-lang-werror + # + save_ac_c_werror_flag="$ac_c_werror_flag" + ac_c_werror_flag=yes + # + # We use AC_LANG_SOURCE() so that we can control the complete + # content of the program being compiled. We do not, for example, + # want the default "int main()" that AC_LANG_PROGRAM() generates, + # as it will generate a warning with -Wold-style-definition, meaning + # that we would treat it as not working, as the test will fail if + # *any* error output, including a warning due to the flag we're + # testing, is generated; see + # + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # + # This may, as per those two messages, be fixed in autoconf 2.70, + # but we only require 2.64 or newer for now. + # + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int main(void) { return 0; } +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + can_add_to_cflags=yes + # + # The compile supports this; do we have some C code for + # which the warning should *not* appear? + # We test the fourth argument because the third argument + # could contain quotes, breaking the test. + # + if test "x" != "x" + then + CFLAGS="$CFLAGS $ac_lbl_cc_force_warning_errors" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -Wpointer-sign " >&5 +$as_echo_n "checking whether -Wpointer-sign ... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + # + # Not a problem. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +else + + # + # A problem. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + can_add_to_cflags=no + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi + CFLAGS="$save_CFLAGS" + if test x"$can_add_to_cflags" = "xyes" + then + V_CCOPT="$V_CCOPT -Wpointer-sign" + fi + +else + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + CFLAGS="$save_CFLAGS" + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag="$save_ac_c_werror_flag" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports the -Wshadow option" >&5 $as_echo_n "checking whether the compiler supports the -Wshadow option... " >&6; } save_CFLAGS="$CFLAGS" - if expr "x-Wshadow" : "x-W.*" >/dev/null - then - CFLAGS="$CFLAGS $ac_lbl_unknown_warning_option_error -Wshadow" - elif expr "x-Wshadow" : "x-f.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -Wshadow" - elif expr "x-Wshadow" : "x-m.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -Wshadow" - else - CFLAGS="$CFLAGS -Wshadow" - fi + CFLAGS="$CFLAGS -Wshadow" + # + # XXX - yes, this depends on the way AC_LANG_WERROR works, + # but no mechanism is provided to turn AC_LANG_WERROR on + # *and then turn it back off*, so that we *only* do it when + # testing compiler options - 15 years after somebody asked + # for it: + # + # https://autoconf.gnu.narkive.com/gTAVmfKD/how-to-cancel-flags-set-by-ac-lang-werror + # + save_ac_c_werror_flag="$ac_c_werror_flag" + ac_c_werror_flag=yes + # + # We use AC_LANG_SOURCE() so that we can control the complete + # content of the program being compiled. We do not, for example, + # want the default "int main()" that AC_LANG_PROGRAM() generates, + # as it will generate a warning with -Wold-style-definition, meaning + # that we would treat it as not working, as the test will fail if + # *any* error output, including a warning due to the flag we're + # testing, is generated; see + # + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # + # This may, as per those two messages, be fixed in autoconf 2.70, + # but we only require 2.64 or newer for now. + # cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - -int -main () -{ -return 0 - ; - return 0; -} +int main(void) { return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : @@ -9636,33 +11738,42 @@ $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag="$save_ac_c_werror_flag" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports the -Wsign-compare option" >&5 $as_echo_n "checking whether the compiler supports the -Wsign-compare option... " >&6; } save_CFLAGS="$CFLAGS" - if expr "x-Wsign-compare" : "x-W.*" >/dev/null - then - CFLAGS="$CFLAGS $ac_lbl_unknown_warning_option_error -Wsign-compare" - elif expr "x-Wsign-compare" : "x-f.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -Wsign-compare" - elif expr "x-Wsign-compare" : "x-m.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -Wsign-compare" - else - CFLAGS="$CFLAGS -Wsign-compare" - fi + CFLAGS="$CFLAGS -Wsign-compare" + # + # XXX - yes, this depends on the way AC_LANG_WERROR works, + # but no mechanism is provided to turn AC_LANG_WERROR on + # *and then turn it back off*, so that we *only* do it when + # testing compiler options - 15 years after somebody asked + # for it: + # + # https://autoconf.gnu.narkive.com/gTAVmfKD/how-to-cancel-flags-set-by-ac-lang-werror + # + save_ac_c_werror_flag="$ac_c_werror_flag" + ac_c_werror_flag=yes + # + # We use AC_LANG_SOURCE() so that we can control the complete + # content of the program being compiled. We do not, for example, + # want the default "int main()" that AC_LANG_PROGRAM() generates, + # as it will generate a warning with -Wold-style-definition, meaning + # that we would treat it as not working, as the test will fail if + # *any* error output, including a warning due to the flag we're + # testing, is generated; see + # + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # + # This may, as per those two messages, be fixed in autoconf 2.70, + # but we only require 2.64 or newer for now. + # cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - -int -main () -{ -return 0 - ; - return 0; -} +int main(void) { return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : @@ -9718,33 +11829,42 @@ $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag="$save_ac_c_werror_flag" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports the -Wstrict-prototypes option" >&5 $as_echo_n "checking whether the compiler supports the -Wstrict-prototypes option... " >&6; } save_CFLAGS="$CFLAGS" - if expr "x-Wstrict-prototypes" : "x-W.*" >/dev/null - then - CFLAGS="$CFLAGS $ac_lbl_unknown_warning_option_error -Wstrict-prototypes" - elif expr "x-Wstrict-prototypes" : "x-f.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -Wstrict-prototypes" - elif expr "x-Wstrict-prototypes" : "x-m.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -Wstrict-prototypes" - else - CFLAGS="$CFLAGS -Wstrict-prototypes" - fi + CFLAGS="$CFLAGS -Wstrict-prototypes" + # + # XXX - yes, this depends on the way AC_LANG_WERROR works, + # but no mechanism is provided to turn AC_LANG_WERROR on + # *and then turn it back off*, so that we *only* do it when + # testing compiler options - 15 years after somebody asked + # for it: + # + # https://autoconf.gnu.narkive.com/gTAVmfKD/how-to-cancel-flags-set-by-ac-lang-werror + # + save_ac_c_werror_flag="$ac_c_werror_flag" + ac_c_werror_flag=yes + # + # We use AC_LANG_SOURCE() so that we can control the complete + # content of the program being compiled. We do not, for example, + # want the default "int main()" that AC_LANG_PROGRAM() generates, + # as it will generate a warning with -Wold-style-definition, meaning + # that we would treat it as not working, as the test will fail if + # *any* error output, including a warning due to the flag we're + # testing, is generated; see + # + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # + # This may, as per those two messages, be fixed in autoconf 2.70, + # but we only require 2.64 or newer for now. + # cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - -int -main () -{ -return 0 - ; - return 0; -} +int main(void) { return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : @@ -9800,33 +11920,42 @@ $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag="$save_ac_c_werror_flag" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports the -Wunused-parameter option" >&5 $as_echo_n "checking whether the compiler supports the -Wunused-parameter option... " >&6; } save_CFLAGS="$CFLAGS" - if expr "x-Wunused-parameter" : "x-W.*" >/dev/null - then - CFLAGS="$CFLAGS $ac_lbl_unknown_warning_option_error -Wunused-parameter" - elif expr "x-Wunused-parameter" : "x-f.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -Wunused-parameter" - elif expr "x-Wunused-parameter" : "x-m.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -Wunused-parameter" - else - CFLAGS="$CFLAGS -Wunused-parameter" - fi + CFLAGS="$CFLAGS -Wunused-parameter" + # + # XXX - yes, this depends on the way AC_LANG_WERROR works, + # but no mechanism is provided to turn AC_LANG_WERROR on + # *and then turn it back off*, so that we *only* do it when + # testing compiler options - 15 years after somebody asked + # for it: + # + # https://autoconf.gnu.narkive.com/gTAVmfKD/how-to-cancel-flags-set-by-ac-lang-werror + # + save_ac_c_werror_flag="$ac_c_werror_flag" + ac_c_werror_flag=yes + # + # We use AC_LANG_SOURCE() so that we can control the complete + # content of the program being compiled. We do not, for example, + # want the default "int main()" that AC_LANG_PROGRAM() generates, + # as it will generate a warning with -Wold-style-definition, meaning + # that we would treat it as not working, as the test will fail if + # *any* error output, including a warning due to the flag we're + # testing, is generated; see + # + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # + # This may, as per those two messages, be fixed in autoconf 2.70, + # but we only require 2.64 or newer for now. + # cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - -int -main () -{ -return 0 - ; - return 0; -} +int main(void) { return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : @@ -9882,33 +12011,42 @@ $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag="$save_ac_c_werror_flag" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports the -Wused-but-marked-unused option" >&5 $as_echo_n "checking whether the compiler supports the -Wused-but-marked-unused option... " >&6; } save_CFLAGS="$CFLAGS" - if expr "x-Wused-but-marked-unused" : "x-W.*" >/dev/null - then - CFLAGS="$CFLAGS $ac_lbl_unknown_warning_option_error -Wused-but-marked-unused" - elif expr "x-Wused-but-marked-unused" : "x-f.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -Wused-but-marked-unused" - elif expr "x-Wused-but-marked-unused" : "x-m.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -Wused-but-marked-unused" - else - CFLAGS="$CFLAGS -Wused-but-marked-unused" - fi + CFLAGS="$CFLAGS -Wused-but-marked-unused" + # + # XXX - yes, this depends on the way AC_LANG_WERROR works, + # but no mechanism is provided to turn AC_LANG_WERROR on + # *and then turn it back off*, so that we *only* do it when + # testing compiler options - 15 years after somebody asked + # for it: + # + # https://autoconf.gnu.narkive.com/gTAVmfKD/how-to-cancel-flags-set-by-ac-lang-werror + # + save_ac_c_werror_flag="$ac_c_werror_flag" + ac_c_werror_flag=yes + # + # We use AC_LANG_SOURCE() so that we can control the complete + # content of the program being compiled. We do not, for example, + # want the default "int main()" that AC_LANG_PROGRAM() generates, + # as it will generate a warning with -Wold-style-definition, meaning + # that we would treat it as not working, as the test will fail if + # *any* error output, including a warning due to the flag we're + # testing, is generated; see + # + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # + # This may, as per those two messages, be fixed in autoconf 2.70, + # but we only require 2.64 or newer for now. + # cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - -int -main () -{ -return 0 - ; - return 0; -} +int main(void) { return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : @@ -9964,6 +12102,7 @@ $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag="$save_ac_c_werror_flag" # Warns about safeguards added in case the enums are # extended @@ -9996,28 +12135,36 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports the -Wunreachable-code option" >&5 $as_echo_n "checking whether the compiler supports the -Wunreachable-code option... " >&6; } save_CFLAGS="$CFLAGS" - if expr "x-Wunreachable-code" : "x-W.*" >/dev/null - then - CFLAGS="$CFLAGS $ac_lbl_unknown_warning_option_error -Wunreachable-code" - elif expr "x-Wunreachable-code" : "x-f.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -Wunreachable-code" - elif expr "x-Wunreachable-code" : "x-m.*" >/dev/null - then - CFLAGS="$CFLAGS -Werror -Wunreachable-code" - else - CFLAGS="$CFLAGS -Wunreachable-code" - fi + CFLAGS="$CFLAGS -Wunreachable-code" + # + # XXX - yes, this depends on the way AC_LANG_WERROR works, + # but no mechanism is provided to turn AC_LANG_WERROR on + # *and then turn it back off*, so that we *only* do it when + # testing compiler options - 15 years after somebody asked + # for it: + # + # https://autoconf.gnu.narkive.com/gTAVmfKD/how-to-cancel-flags-set-by-ac-lang-werror + # + save_ac_c_werror_flag="$ac_c_werror_flag" + ac_c_werror_flag=yes + # + # We use AC_LANG_SOURCE() so that we can control the complete + # content of the program being compiled. We do not, for example, + # want the default "int main()" that AC_LANG_PROGRAM() generates, + # as it will generate a warning with -Wold-style-definition, meaning + # that we would treat it as not working, as the test will fail if + # *any* error output, including a warning due to the flag we're + # testing, is generated; see + # + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # + # This may, as per those two messages, be fixed in autoconf 2.70, + # but we only require 2.64 or newer for now. + # cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - -int -main () -{ -return 0 - ; - return 0; -} +int main(void) { return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : @@ -10080,6 +12227,98 @@ $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag="$save_ac_c_werror_flag" + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports the -Wshorten-64-to-32 option" >&5 +$as_echo_n "checking whether the compiler supports the -Wshorten-64-to-32 option... " >&6; } + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -Wshorten-64-to-32" + # + # XXX - yes, this depends on the way AC_LANG_WERROR works, + # but no mechanism is provided to turn AC_LANG_WERROR on + # *and then turn it back off*, so that we *only* do it when + # testing compiler options - 15 years after somebody asked + # for it: + # + # https://autoconf.gnu.narkive.com/gTAVmfKD/how-to-cancel-flags-set-by-ac-lang-werror + # + save_ac_c_werror_flag="$ac_c_werror_flag" + ac_c_werror_flag=yes + # + # We use AC_LANG_SOURCE() so that we can control the complete + # content of the program being compiled. We do not, for example, + # want the default "int main()" that AC_LANG_PROGRAM() generates, + # as it will generate a warning with -Wold-style-definition, meaning + # that we would treat it as not working, as the test will fail if + # *any* error output, including a warning due to the flag we're + # testing, is generated; see + # + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # https://www.postgresql.org/message-id/2192993.1591682589%40sss.pgh.pa.us + # + # This may, as per those two messages, be fixed in autoconf 2.70, + # but we only require 2.64 or newer for now. + # + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int main(void) { return 0; } +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + can_add_to_cflags=yes + # + # The compile supports this; do we have some C code for + # which the warning should *not* appear? + # We test the fourth argument because the third argument + # could contain quotes, breaking the test. + # + if test "x" != "x" + then + CFLAGS="$CFLAGS $ac_lbl_cc_force_warning_errors" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -Wshorten-64-to-32 " >&5 +$as_echo_n "checking whether -Wshorten-64-to-32 ... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + # + # Not a problem. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +else + + # + # A problem. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + can_add_to_cflags=no + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi + CFLAGS="$save_CFLAGS" + if test x"$can_add_to_cflags" = "xyes" + then + V_CCOPT="$V_CCOPT -Wshorten-64-to-32" + fi + +else + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + CFLAGS="$save_CFLAGS" + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag="$save_ac_c_werror_flag" fi @@ -10161,7 +12400,7 @@ _ACEOF { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes, with $ac_lbl_dependency_flag" >&5 $as_echo "yes, with $ac_lbl_dependency_flag" >&6; } DEPENDENCY_CFLAG="$ac_lbl_dependency_flag" - MKDEP='${srcdir}/mkdep' + MKDEP='${top_srcdir}/mkdep' else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } @@ -10169,7 +12408,7 @@ $as_echo "no" >&6; } # We can't run mkdep, so have "make depend" do # nothing. # - MKDEP='${srcdir}/nomkdep' + MKDEP='${top_srcdir}/nomkdep' fi rm -rf conftest* else @@ -10179,7 +12418,7 @@ $as_echo "no" >&6; } # We can't run mkdep, so have "make depend" do # nothing. # - MKDEP='${srcdir}/nomkdep' + MKDEP='${top_srcdir}/nomkdep' fi @@ -10268,131 +12507,9 @@ _ACEOF fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if unaligned accesses fail" >&5 -$as_echo_n "checking if unaligned accesses fail... " >&6; } - if ${ac_cv_lbl_unaligned_fail+:} false; then : - $as_echo_n "(cached) " >&6 -else - case "$host_cpu" in - - # - # These are CPU types where: - # - # the CPU faults on an unaligned access, but at least some - # OSes that support that CPU catch the fault and simulate - # the unaligned access (e.g., Alpha/{Digital,Tru64} UNIX) - - # the simulation is slow, so we don't want to use it; - # - # the CPU, I infer (from the old - # - # XXX: should also check that they don't do weird things (like on arm) - # - # comment) doesn't fault on unaligned accesses, but doesn't - # do a normal unaligned fetch, either (e.g., presumably, ARM); - # - # for whatever reason, the test program doesn't work - # (this has been claimed to be the case for several of those - # CPUs - I don't know what the problem is; the problem - # was reported as "the test program dumps core" for SuperH, - # but that's what the test program is *supposed* to do - - # it dumps core before it writes anything, so the test - # for an empty output file should find an empty output - # file and conclude that unaligned accesses don't work). - # - # This run-time test won't work if you're cross-compiling, so - # in order to support cross-compiling for a particular CPU, - # we have to wire in the list of CPU types anyway, as far as - # I know, so perhaps we should just have a set of CPUs on - # which we know it doesn't work, a set of CPUs on which we - # know it does work, and have the script just fail on other - # cpu types and update it when such a failure occurs. - # - alpha*|arm*|bfin*|hp*|mips*|sh*|sparc*|ia64|nv1) - ac_cv_lbl_unaligned_fail=yes - ;; - - *) - cat >conftest.c < -# include -# include - unsigned char a[5] = { 1, 2, 3, 4, 5 }; - main() { - unsigned int i; - pid_t pid; - int status; - /* avoid "core dumped" message */ - pid = fork(); - if (pid < 0) - exit(2); - if (pid > 0) { - /* parent */ - pid = waitpid(pid, &status, 0); - if (pid < 0) - exit(3); - exit(!WIFEXITED(status)); - } - /* child */ - i = *(unsigned int *)&a[1]; - printf("%d\n", i); - exit(0); - } -EOF - ${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS \ - conftest.c $LIBS >/dev/null 2>&1 - if test ! -x conftest ; then - ac_cv_lbl_unaligned_fail=yes - else - ./conftest >conftest.out - if test ! -s conftest.out ; then - ac_cv_lbl_unaligned_fail=yes - else - ac_cv_lbl_unaligned_fail=no - fi - fi - rm -f -r conftest* core core.conftest - ;; - esac -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lbl_unaligned_fail" >&5 -$as_echo "$ac_cv_lbl_unaligned_fail" >&6; } - if test $ac_cv_lbl_unaligned_fail = yes ; then - -$as_echo "#define LBL_ALIGN 1" >>confdefs.h - - fi - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# +# Various Linux-specific mechanisms. +# # Check whether --enable-usb was given. if test "${enable_usb+set}" = set; then : enableval=$enable_usb; @@ -10401,39 +12518,38 @@ else fi -if test "xxx_only" = yes; then - # User requested something-else-only pcap, so they don't - # want USB support. - enable_usb=no -fi +# +# If somebody requested an XXX-only pcap, that doesn't include +# additional mechanisms. +# +if test "xxx_only" != yes; then + case "$host_os" in + linux*) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Linux usbmon USB sniffing support" >&5 +$as_echo_n "checking for Linux usbmon USB sniffing support... " >&6; } + if test "x$enable_usb" != "xno" ; then -if test "x$enable_usb" != "xno" ; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for USB sniffing support" >&5 -$as_echo_n "checking for USB sniffing support... " >&6; } - case "$host_os" in - linux*) +$as_echo "#define PCAP_SUPPORT_LINUX_USBMON 1" >>confdefs.h -$as_echo "#define PCAP_SUPPORT_USB 1" >>confdefs.h - - USB_SRC=pcap-usb-linux.c - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + MODULE_C_SRC="$MODULE_C_SRC pcap-usb-linux.c" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } - ac_usb_dev_name=`udevinfo -q name -p /sys/class/usb_device/usbmon 2>/dev/null` - if test $? -ne 0 ; then - ac_usb_dev_name="usbmon" - fi - -cat >>confdefs.h <<_ACEOF -#define LINUX_USB_MON_DEV "/dev/$ac_usb_dev_name" -_ACEOF - - { $as_echo "$as_me:${as_lineno-$LINENO}: Device for USB sniffing is /dev/$ac_usb_dev_name" >&5 -$as_echo "$as_me: Device for USB sniffing is /dev/$ac_usb_dev_name" >&6;} - # - # Do we have a version of available? - # If so, we might need it for . - # - for ac_header in linux/compiler.h + # + # Note: if the directory for special files is *EVER* somewhere + # other than the UN*X standard of /dev (which will break any + # software that looks for /dev/null or /dev/tty, for example, + # so doing that is *REALLY* not a good idea), please provide + # some mechanism to determine that directory at *run time*, + # rather than *configure time*, so that it works when doinga + # a cross-build, and that works with *multiple* distributions, + # with our without udev, and with multiple versions of udev, + # with udevinfo or udevadm or any other mechanism to get the + # special files directory. + # + # Do we have a version of available? + # If so, we might need it for . + # + for ac_header in linux/compiler.h do : ac_fn_c_check_header_mongrel "$LINENO" "linux/compiler.h" "ac_cv_header_linux_compiler_h" "$ac_includes_default" if test "x$ac_cv_header_linux_compiler_h" = xyes; then : @@ -10445,11 +12561,11 @@ fi done - if test "$ac_cv_header_linux_compiler_h" = yes; then - # - # Yes - include it when testing for . - # - for ac_header in linux/usbdevice_fs.h + if test "$ac_cv_header_linux_compiler_h" = yes; then + # + # Yes - include it when testing for . + # + for ac_header in linux/usbdevice_fs.h do : ac_fn_c_check_header_compile "$LINENO" "linux/usbdevice_fs.h" "ac_cv_header_linux_usbdevice_fs_h" "#include " @@ -10462,8 +12578,8 @@ fi done - else - for ac_header in linux/usbdevice_fs.h + else + for ac_header in linux/usbdevice_fs.h do : ac_fn_c_check_header_mongrel "$LINENO" "linux/usbdevice_fs.h" "ac_cv_header_linux_usbdevice_fs_h" "$ac_includes_default" if test "x$ac_cv_header_linux_usbdevice_fs_h" = xyes; then : @@ -10475,20 +12591,20 @@ fi done - fi - if test "$ac_cv_header_linux_usbdevice_fs_h" = yes; then - # - # OK, does it define bRequestType? Older versions of the kernel - # define fields with names like "requesttype, "request", and - # "value", rather than "bRequestType", "bRequest", and - # "wValue". - # - ac_fn_c_check_member "$LINENO" "struct usbdevfs_ctrltransfer" "bRequestType" "ac_cv_member_struct_usbdevfs_ctrltransfer_bRequestType" " - $ac_includes_default - #ifdef HAVE_LINUX_COMPILER_H - #include - #endif - #include + fi + if test "$ac_cv_header_linux_usbdevice_fs_h" = yes; then + # + # OK, does it define bRequestType? Older versions of the kernel + # define fields with names like "requesttype, "request", and + # "value", rather than "bRequestType", "bRequest", and + # "wValue". + # + ac_fn_c_check_member "$LINENO" "struct usbdevfs_ctrltransfer" "bRequestType" "ac_cv_member_struct_usbdevfs_ctrltransfer_bRequestType" " + $ac_includes_default + #ifdef HAVE_LINUX_COMPILER_H + #include + #endif + #include " if test "x$ac_cv_member_struct_usbdevfs_ctrltransfer_bRequestType" = xyes; then : @@ -10500,44 +12616,23 @@ _ACEOF fi - fi - ;; - freebsd*) - # - # This just uses BPF in FreeBSD 8.4 and later; we don't need - # to check for anything special for capturing. - # - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes, in FreeBSD 8.4 and later" >&5 -$as_echo "yes, in FreeBSD 8.4 and later" >&6; } - ;; - - *) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } - ;; -esac -fi + fi - - -if test "xxx_only" != yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the platform could support netfilter sniffing" >&5 -$as_echo_n "checking whether the platform could support netfilter sniffing... " >&6; } - case "$host_os" in - linux*) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - # - # Life's too short to deal with trying to get this to compile - # if you don't get the right types defined with - # __KERNEL_STRICT_NAMES getting defined by some other include. - # - # Check whether the includes Just Work. If not, don't turn on - # netfilter support. - # - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we can compile the netfilter support" >&5 + # + # Life's too short to deal with trying to get this to compile + # if you don't get the right types defined with + # __KERNEL_STRICT_NAMES getting defined by some other include. + # + # Check whether the includes Just Work. If not, don't turn on + # netfilter support. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we can compile the netfilter support" >&5 $as_echo_n "checking whether we can compile the netfilter support... " >&6; } - if ${ac_cv_netfilter_can_compile+:} false; then : + if ${ac_cv_netfilter_can_compile+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -10569,20 +12664,16 @@ fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_netfilter_can_compile" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_netfilter_can_compile" >&5 $as_echo "$ac_cv_netfilter_can_compile" >&6; } - if test $ac_cv_netfilter_can_compile = yes ; then + if test $ac_cv_netfilter_can_compile = yes ; then $as_echo "#define PCAP_SUPPORT_NETFILTER 1" >>confdefs.h - NETFILTER_SRC=pcap-netfilter-linux.c - fi - ;; - *) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - ;; - esac + MODULE_C_SRC="$MODULE_C_SRC pcap-netfilter-linux.c" + fi + ;; + esac fi @@ -10635,10 +12726,379 @@ $as_echo "$ac_cv_net_netmap_user_can_compile" >&6; } $as_echo "#define PCAP_SUPPORT_NETMAP 1" >>confdefs.h - NETMAP_SRC=pcap-netmap.c + MODULE_C_SRC="$MODULE_C_SRC pcap-netmap.c" + fi + +fi + +# Check for DPDK support. + +# Check whether --with-dpdk was given. +if test "${with_dpdk+set}" = set; then : + withval=$with_dpdk; + if test "$withval" = no + then + # User doesn't want DPDK support. + want_dpdk=no + elif test "$withval" = yes + then + # User wants DPDK support but hasn't specified a directory. + want_dpdk=yes + else + # User wants DPDK support and has specified a directory, + # so use the provided value. + want_dpdk=yes + dpdk_dir=$withval + fi + +else + + if test "$V_PCAP" = dpdk; then + # User requested DPDK-only libpcap, so we'd better have + # the DPDK API. + want_dpdk=yes + elif test "xxx_only" = yes; then + # User requested something-else-only pcap, so they don't + # want DPDK support. + want_dpdk=no + else + # + # Use DPDK API if present, otherwise don't + # + want_dpdk=ifpresent + fi + +fi + + +if test "$want_dpdk" != no; then + # + # The user didn't explicitly say they don't want DPDK, + # so see if we have it. + # + # We only try to find it using pkg-config; DPDK is *SO* + # complicated - DPDK 19.02, for example, has about 117(!) + # libraries, and the precise set of libraries required has + # changed over time - so attempting to guess which libraries + # you need, and hardcoding that in an attempt to find the + # libraries without DPDK, rather than relying on DPDK to + # tell you, with a .pc file, what libraries are needed, + # is *EXTREMELY* fragile and has caused some bug reports, + # so we're just not going to do it. + # + # If that causes a problem, the only thing we will do is + # accept an alternative way of finding the appropriate + # library set for the installed version of DPDK that is + # as robust as pkg-config (i.e., it had better work as well + # as pkg-config with *ALL* versions of DPDK that provide a + # libdpdk.pc file). + # + # If --with-dpdk={path} was specified, add {path}/pkgconfig + # to PKG_CONFIG_PATH, so we look for the .pc file there, + # first. + # + save_PKG_CONFIG_PATH="$PKG_CONFIG_PATH" + if test -n "$dpdk_dir"; then + PKG_CONFIG_PATH="$dpdk_dir:$PKG_CONFIG_PATH" fi +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libdpdk with pkg-config" >&5 +$as_echo_n "checking for libdpdk with pkg-config... " >&6; } + +if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libdpdk\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libdpdk") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + + # + # The package was found, so try to get its C flags and + # libraries. + # + if test -n "$DPDK_CFLAGS"; then + pkg_cv_DPDK_CFLAGS="$DPDK_CFLAGS" + elif test -n "$PKG_CONFIG"; then + +if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libdpdk\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libdpdk") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_DPDK_CFLAGS=`$PKG_CONFIG --cflags "libdpdk" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + if test -n "$DPDK_LIBS"; then + pkg_cv_DPDK_LIBS="$DPDK_LIBS" + elif test -n "$PKG_CONFIG"; then + +if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libdpdk\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libdpdk") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_DPDK_LIBS=`$PKG_CONFIG --libs "libdpdk" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + if test -n "$DPDK_LIBS_STATIC"; then + pkg_cv_DPDK_LIBS_STATIC="$DPDK_LIBS_STATIC" + elif test -n "$PKG_CONFIG"; then + +if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libdpdk\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libdpdk") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_DPDK_LIBS_STATIC=`$PKG_CONFIG --libs --static "libdpdk" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + + if test $pkg_failed = yes; then + # + # That failed - report an error. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: result: error" >&5 +$as_echo "error" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + DPDK_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libdpdk" 2>&1` + else + DPDK_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libdpdk" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$DPDK_PKG_ERRORS" >&5 + + as_fn_error $? "Package requirements (libdpdk) were not met: + +$DPDK_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + + +Alternatively, you may set the environment variables DPDK_CFLAGS +and DPDK_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details." "$LINENO" 5 + elif test $pkg_failed = untried; then + # + # We don't have pkg-config, so it didn't work. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found (pkg-config not found)" >&5 +$as_echo "not found (pkg-config not found)" >&6; } + else + # + # We found the package. + # + DPDK_CFLAGS=$pkg_cv_DPDK_CFLAGS + DPDK_LIBS=$pkg_cv_DPDK_LIBS + DPDK_LIBS_STATIC=$pkg_cv_DPDK_LIBS_STATIC + { $as_echo "$as_me:${as_lineno-$LINENO}: result: found" >&5 +$as_echo "found" >&6; } + + found_dpdk=yes + + fi +else + + # + # The package isn't present. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5 +$as_echo "not found" >&6; } + +fi + + PKG_CONFIG_PATH="$save_PKG_CONFIG_PATH" + + # + # Did we find DPDK? + # + if test "$found_dpdk" = yes; then + # + # Found it. + # + # We call rte_eth_dev_count_avail(), and older versions + # of DPDK didn't have it, so check for it. + # + + save_CFLAGS="$CFLAGS" + save_LIBS="$LIBS" + save_LDFLAGS="$LDFLAGS" + + CFLAGS="$CFLAGS $DPDK_CFLAGS" + LIBS="$LIBS $DPDK_LIBS" + ac_fn_c_check_func "$LINENO" "rte_eth_dev_count_avail" "ac_cv_func_rte_eth_dev_count_avail" +if test "x$ac_cv_func_rte_eth_dev_count_avail" = xyes; then : + +fi + + + CFLAGS="$save_CFLAGS" + LIBS="$save_LIBS" + LDFLAGS="$save_LDFLAGS" + + fi + + if test "$ac_cv_func_rte_eth_dev_count_avail" = yes; then + # + # We found a usable DPDK. + # + # Check whether the rte_ether.h file defines + # struct ether_addr or struct rte_ether_addr. + # + # ("API compatibility? That's for losers!") + # + + save_CFLAGS="$CFLAGS" + save_LIBS="$LIBS" + save_LDFLAGS="$LDFLAGS" + + CFLAGS="$CFLAGS $DPDK_CFLAGS" + LIBS="$LIBS $DPDK_LIBS" + ac_fn_c_check_type "$LINENO" "struct rte_ether_addr" "ac_cv_type_struct_rte_ether_addr" " + #include + +" +if test "x$ac_cv_type_struct_rte_ether_addr" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_RTE_ETHER_ADDR 1 +_ACEOF + + +fi + + + CFLAGS="$save_CFLAGS" + LIBS="$save_LIBS" + LDFLAGS="$save_LDFLAGS" + + + # + # We can build with DPDK. + # + V_INCLS="$V_INCLS $DPDK_CFLAGS" + ADDITIONAL_LIBS="$ADDITIONAL_LIBS $DPDK_LIBS" + ADDITIONAL_LIBS_STATIC="$ADDITIONAL_LIBS_STATIC $DPDK_LIBS_STATIC" + REQUIRES_PRIVATE="$REQUIRES_PRIVATE libdpdk" + +$as_echo "#define PCAP_SUPPORT_DPDK 1" >>confdefs.h + + if test $V_PCAP != dpdk ; then + MODULE_C_SRC="$MODULE_C_SRC pcap-dpdk.c" + fi + else + # + # We didn't find a usable DPDK. + # If we required it (with --with-dpdk or --with-pcap=dpdk), + # fail with an appropriate message telling the user what + # the problem was, otherwise note the problem with a + # warning. + # + if test "$found_dpdk" != yes; then + # + # Not found with pkg-config. Note that we + # require that DPDK must be findable with + # pkg-config. + # + if test "$V_PCAP" = dpdk; then + # + # User requested DPDK-only capture support. + # + as_fn_error $? "DPDK support requested with --with-pcap=dpdk, but +we couldn't find DPDK with pkg-config. Make sure that pkg-config is +installed, that DPDK 18.02.2 or later is installed, and that DPDK +provides a .pc file." "$LINENO" 5 + fi + + if test "$want_dpdk" = yes; then + # + # User requested that libpcap include + # DPDK capture support. + # + as_fn_error $? "DPDK support requested with --with-dpdk, but we +couldn't find DPDK with pkg-config. Make sure that pkg-config +is installed, that DPDK 18.02.2 or later is installed, and that +DPDK provides .pc file." "$LINENO" 5 + fi + + # + # User didn't indicate whether they wanted DPDK + # or not; just warn why we didn't find it. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: We couldn't find DPDK with pkg-config. If +you want DPDK support, make sure that pkg-config is installed, +that DPDK 18.02.2 or later is installed, and that DPDK provides a +.pc file." >&5 +$as_echo "$as_me: WARNING: We couldn't find DPDK with pkg-config. If +you want DPDK support, make sure that pkg-config is installed, +that DPDK 18.02.2 or later is installed, and that DPDK provides a +.pc file." >&2;} + elif test "$ac_cv_func_rte_eth_dev_count_avail" != yes; then + # + # Found with pkg-config, but we couldn't compile + # a program that calls rte_eth_dev_count(); we + # probably have the developer package installed, + # but don't have a sufficiently recent version + # of DPDK. Note that we need a sufficiently + # recent version of DPDK. + # + if test "$V_PCAP" = dpdk; then + # + # User requested DPDK-only capture support. + # + as_fn_error $? "DPDK support requested with --with-pcap=dpdk, but we +can't compile libpcap with DPDK. Make sure that DPDK 18.02.2 or later +is installed." "$LINENO" 5 + fi + + if test "$want_dpdk" = yes; then + # + # User requested that libpcap include + # DPDK capture support. + # + as_fn_error $? "DPDK support requested with --with-dpdk, but +we can't compile libpcap with DPDK. Make sure that DPDK 18.02.2 +or later is DPDK is installed." "$LINENO" 5 + fi + + # + # User didn't indicate whether they wanted DPDK + # or not; just warn why we didn't find it. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: DPDK was found, but we can't compile libpcap with it. +Make sure that DPDK 18.02.2 or later is installed." >&5 +$as_echo "$as_me: WARNING: DPDK was found, but we can't compile libpcap with it. +Make sure that DPDK 18.02.2 or later is installed." >&2;} + fi + fi fi @@ -10669,7 +13129,7 @@ if test "x$ac_cv_header_bluetooth_bluetooth_h" = xyes; then : $as_echo "#define PCAP_SUPPORT_BT 1" >>confdefs.h - BT_SRC=pcap-bt-linux.c + MODULE_C_SRC="$MODULE_C_SRC pcap-bt-linux.c" { $as_echo "$as_me:${as_lineno-$LINENO}: Bluetooth sniffing is supported" >&5 $as_echo "$as_me: Bluetooth sniffing is supported" >&6;} ac_lbl_bluetooth_available=yes @@ -10721,7 +13181,7 @@ $as_echo "yes" >&6; } $as_echo "#define PCAP_SUPPORT_BT_MONITOR /**/" >>confdefs.h - BT_MONITOR_SRC=pcap-bt-monitor-linux.c + MODULE_C_SRC="$MODULE_C_SRC pcap-bt-monitor-linux.c" else @@ -10763,8 +13223,6 @@ $as_echo "$as_me: no Bluetooth sniffing support implemented for $host_os" >&6;} ;; esac - - fi # Check whether --enable-dbus was given. @@ -10814,66 +13272,138 @@ if test "x$enable_dbus" != "xno"; then fi if test "x$enable_dbus" != "xno"; then - # Extract the first word of "pkg-config", so it can be a program name with args. -set dummy pkg-config; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_PKGCONFIG+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$PKGCONFIG"; then - ac_cv_prog_PKGCONFIG="$PKGCONFIG" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_PKGCONFIG="pkg-config" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - test -z "$ac_cv_prog_PKGCONFIG" && ac_cv_prog_PKGCONFIG="no" -fi -fi -PKGCONFIG=$ac_cv_prog_PKGCONFIG -if test -n "$PKGCONFIG"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKGCONFIG" >&5 -$as_echo "$PKGCONFIG" >&6; } + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dbus-1 with pkg-config" >&5 +$as_echo_n "checking for dbus-1 with pkg-config... " >&6; } + +if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"dbus-1\""; } >&5 + ($PKG_CONFIG --exists --print-errors "dbus-1") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + + # + # The package was found, so try to get its C flags and + # libraries. + # + if test -n "$DBUS_CFLAGS"; then + pkg_cv_DBUS_CFLAGS="$DBUS_CFLAGS" + elif test -n "$PKG_CONFIG"; then + +if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"dbus-1\""; } >&5 + ($PKG_CONFIG --exists --print-errors "dbus-1") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_DBUS_CFLAGS=`$PKG_CONFIG --cflags "dbus-1" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + pkg_failed=yes +fi + else + pkg_failed=untried +fi + if test -n "$DBUS_LIBS"; then + pkg_cv_DBUS_LIBS="$DBUS_LIBS" + elif test -n "$PKG_CONFIG"; then + +if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"dbus-1\""; } >&5 + ($PKG_CONFIG --exists --print-errors "dbus-1") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_DBUS_LIBS=`$PKG_CONFIG --libs "dbus-1" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + if test -n "$DBUS_LIBS_STATIC"; then + pkg_cv_DBUS_LIBS_STATIC="$DBUS_LIBS_STATIC" + elif test -n "$PKG_CONFIG"; then + +if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"dbus-1\""; } >&5 + ($PKG_CONFIG --exists --print-errors "dbus-1") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_DBUS_LIBS_STATIC=`$PKG_CONFIG --libs --static "dbus-1" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried fi - if test "x$PKGCONFIG" != "xno"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for D-Bus" >&5 -$as_echo_n "checking for D-Bus... " >&6; } - if "$PKGCONFIG" dbus-1; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - DBUS_CFLAGS=`"$PKGCONFIG" --cflags dbus-1` - DBUS_LIBS=`"$PKGCONFIG" --libs dbus-1` - save_CFLAGS="$CFLAGS" - save_LIBS="$LIBS" - CFLAGS="$CFLAGS $DBUS_CFLAGS" - LIBS="$LIBS $DBUS_LIBS" - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the D-Bus library defines dbus_connection_read_write" >&5 + + if test $pkg_failed = yes; then + # + # That failed - report an error. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: result: error" >&5 +$as_echo "error" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + DBUS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "dbus-1" 2>&1` + else + DBUS_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "dbus-1" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$DBUS_PKG_ERRORS" >&5 + + + if test "x$enable_dbus" = "xyes"; then + as_fn_error $? "--enable-dbus was given, but the dbus-1 package is not installed" "$LINENO" 5 + fi + + elif test $pkg_failed = untried; then + # + # We don't have pkg-config, so it didn't work. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found (pkg-config not found)" >&5 +$as_echo "not found (pkg-config not found)" >&6; } + else + # + # We found the package. + # + DBUS_CFLAGS=$pkg_cv_DBUS_CFLAGS + DBUS_LIBS=$pkg_cv_DBUS_LIBS + DBUS_LIBS_STATIC=$pkg_cv_DBUS_LIBS_STATIC + { $as_echo "$as_me:${as_lineno-$LINENO}: result: found" >&5 +$as_echo "found" >&6; } + + + save_CFLAGS="$CFLAGS" + save_LIBS="$LIBS" + save_LDFLAGS="$LDFLAGS" + + CFLAGS="$CFLAGS $DBUS_CFLAGS" + LIBS="$LIBS $DBUS_LIBS" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the D-Bus library defines dbus_connection_read_write" >&5 $as_echo_n "checking whether the D-Bus library defines dbus_connection_read_write... " >&6; } - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include - #include - #include + #include + #include - #include + #include int main () { @@ -10884,35 +13414,44 @@ return dbus_connection_read_write(NULL, 0); _ACEOF if ac_fn_c_try_link "$LINENO"; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } $as_echo "#define PCAP_SUPPORT_DBUS 1" >>confdefs.h - DBUS_SRC=pcap-dbus.c - V_INCLS="$V_INCLS $DBUS_CFLAGS" + MODULE_C_SRC="$MODULE_C_SRC pcap-dbus.c" + V_INCLS="$V_INCLS $DBUS_CFLAGS" + ADDITIONAL_LIBS="$ADDITIONAL_LIBS $DBUS_LIBS" + ADDITIONAL_LIBS_STATIC="$ADDITIONAL_LIBS_STATIC $DBUS_LIBS_STATIC" + REQUIRES_PRIVATE="$REQUIRES_PRIVATE dbus-1" else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } - if test "x$enable_dbus" = "xyes"; then - as_fn_error $? "--enable-dbus was given, but the D-Bus library doesn't define dbus_connection_read_write()" "$LINENO" 5 - fi - LIBS="$save_LIBS" + if test "x$enable_dbus" = "xyes"; then + as_fn_error $? "--enable-dbus was given, but the D-Bus library doesn't define dbus_connection_read_write()" "$LINENO" 5 + fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext - CFLAGS="$save_CFLAGS" - else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - if test "x$enable_dbus" = "xyes"; then - as_fn_error $? "--enable-dbus was given, but the dbus-1 package is not installed" "$LINENO" 5 - fi - fi + + CFLAGS="$save_CFLAGS" + LIBS="$save_LIBS" + LDFLAGS="$save_LDFLAGS" + + fi +else + + # + # The package isn't present. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5 +$as_echo "not found" >&6; } + +fi fi @@ -10932,7 +13471,144 @@ if test "xxx_only" = yes; then fi if test "x$enable_rdma" != "xno"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ibv_get_device_list in -libverbs" >&5 + + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libibverbs with pkg-config" >&5 +$as_echo_n "checking for libibverbs with pkg-config... " >&6; } + +if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libibverbs\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libibverbs") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + + # + # The package was found, so try to get its C flags and + # libraries. + # + if test -n "$LIBIBVERBS_CFLAGS"; then + pkg_cv_LIBIBVERBS_CFLAGS="$LIBIBVERBS_CFLAGS" + elif test -n "$PKG_CONFIG"; then + +if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libibverbs\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libibverbs") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_LIBIBVERBS_CFLAGS=`$PKG_CONFIG --cflags "libibverbs" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + if test -n "$LIBIBVERBS_LIBS"; then + pkg_cv_LIBIBVERBS_LIBS="$LIBIBVERBS_LIBS" + elif test -n "$PKG_CONFIG"; then + +if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libibverbs\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libibverbs") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_LIBIBVERBS_LIBS=`$PKG_CONFIG --libs "libibverbs" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + if test -n "$LIBIBVERBS_LIBS_STATIC"; then + pkg_cv_LIBIBVERBS_LIBS_STATIC="$LIBIBVERBS_LIBS_STATIC" + elif test -n "$PKG_CONFIG"; then + +if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libibverbs\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libibverbs") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_LIBIBVERBS_LIBS_STATIC=`$PKG_CONFIG --libs --static "libibverbs" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + + if test $pkg_failed = yes; then + # + # That failed - report an error. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: result: error" >&5 +$as_echo "error" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + LIBIBVERBS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libibverbs" 2>&1` + else + LIBIBVERBS_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libibverbs" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$LIBIBVERBS_PKG_ERRORS" >&5 + + as_fn_error $? "Package requirements (libibverbs) were not met: + +$LIBIBVERBS_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + + +Alternatively, you may set the environment variables LIBIBVERBS_CFLAGS +and LIBIBVERBS_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details." "$LINENO" 5 + elif test $pkg_failed = untried; then + # + # We don't have pkg-config, so it didn't work. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found (pkg-config not found)" >&5 +$as_echo "not found (pkg-config not found)" >&6; } + else + # + # We found the package. + # + LIBIBVERBS_CFLAGS=$pkg_cv_LIBIBVERBS_CFLAGS + LIBIBVERBS_LIBS=$pkg_cv_LIBIBVERBS_LIBS + LIBIBVERBS_LIBS_STATIC=$pkg_cv_LIBIBVERBS_LIBS_STATIC + { $as_echo "$as_me:${as_lineno-$LINENO}: result: found" >&5 +$as_echo "found" >&6; } + + found_libibverbs=yes + LIBIBVERBS_REQUIRES_PRIVATE="libibverbs" + + fi +else + + # + # The package isn't present. + # + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5 +$as_echo "not found" >&6; } + +fi + + + if test "x$found_libibverbs" != "xyes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ibv_get_device_list in -libverbs" >&5 $as_echo_n "checking for ibv_get_device_list in -libverbs... " >&6; } if ${ac_cv_lib_ibverbs_ibv_get_device_list+:} false; then : $as_echo_n "(cached) " >&6 @@ -10970,6 +13646,30 @@ fi $as_echo "$ac_cv_lib_ibverbs_ibv_get_device_list" >&6; } if test "x$ac_cv_lib_ibverbs_ibv_get_device_list" = xyes; then : + found_libibverbs=yes + LIBIBVERBS_CFLAGS="" + LIBIBVERBS_LIBS="-libverbs" + # XXX - at least on Ubuntu 20.04, there are many more + # libraries needed; is there any platform where + # libibverbs is available but where pkg-config isn't + # available or libibverbs doesn't use it? If not, + # we should only use pkg-config for it. + LIBIBVERBS_LIBS_STATIC="-libverbs" + LIBIBVERBS_LIBS_PRIVATE="-libverbs" + + +fi + + fi + + if test "x$found_libibverbs" = "xyes"; then + + save_CFLAGS="$CFLAGS" + save_LIBS="$LIBS" + save_LDFLAGS="$LDFLAGS" + + CFLAGS="$CFLAGS $LIBIBVERBS_CFLAGS" + LIBS="$LIBS $LIBIBVERBS_LIBS" ac_fn_c_check_header_mongrel "$LINENO" "infiniband/verbs.h" "ac_cv_header_infiniband_verbs_h" "$ac_includes_default" if test "x$ac_cv_header_infiniband_verbs_h" = xyes; then : @@ -11006,11 +13706,7 @@ if ac_fn_c_try_link "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } - -$as_echo "#define PCAP_SUPPORT_RDMASNIFF /**/" >>confdefs.h - - RDMA_SRC=pcap-rdmasniff.c - LIBS="-libverbs $LIBS" + found_usable_libibverbs=yes else @@ -11026,10 +13722,80 @@ fi + CFLAGS="$save_CFLAGS" + LIBS="$save_LIBS" + LDFLAGS="$save_LDFLAGS" + + fi + + if test "x$found_usable_libibverbs" = "xyes" + then + +$as_echo "#define PCAP_SUPPORT_RDMASNIFF /**/" >>confdefs.h + + MODULE_C_SRC="$MODULE_C_SRC pcap-rdmasniff.c" + CFLAGS="$LIBIBVERBS_CFLAGS $CFLAGS" + ADDITIONAL_LIBS="$LIBIBVERBS_LIBS $ADDITIONAL_LIBS" + ADDITIONAL_LIBS_STATIC="$LIBIBVERBS_LIBS_STATIC $ADDITIONAL_LIBS_STATIC" + LIBS_PRIVATE="$LIBIBVERBS_LIBS_PRIVATE $LIBS_PRIVATE" + REQUIRES_PRIVATE="$REQUIRES_PRIVATE $LIBIBVERBS_REQUIRES_PRIVATE" + fi + fi +# +# If this is a platform where we need to have the .pc file and +# pcap-config script supply an rpath option to specify the directory +# in which the libpcap shared library is installed, and the install +# prefix /usr (meaning we're not installing a system library), provide +# the rpath option. +# +# (We must check $prefix, as $libdir isn't necessarily /usr/lib in this +# case - for example, Linux distributions for 64-bit platforms that +# also provide support for binaries for a 32-bit version of the +# platform may put the 64-bit libraries, the 32-bit libraries, or both +# in directories other than /usr/lib.) +# +# In AIX, do we have to do this? +# +# In Darwin-based OSes, the full paths of the shared libraries with +# which the program was linked are stored in the executable, so we don't +# need to provide an rpath option. +# +# With the HP-UX linker, directories specified with -L are, by default, +# added to the run-time search path, so we don't need to supply them. +# +# For Tru64 UNIX, "-rpath" works with DEC's^WCompaq's^WHP's C compiler +# for Alpha, but isn't documented as working with GCC, and no GCC- +# compatible option is documented as working with the DEC compiler. +# If anybody needs this on Tru64/Alpha, they're welcome to figure out a +# way to make it work. +# +# This must *not* depend on the compiler, as, on platforms where there's +# a GCC-compatible compiler and a vendor compiler, we need to work with +# both. +# +if test "$prefix" != "/usr"; then + case "$host_os" in + freebsd*|netbsd*|openbsd*|dragonfly*|linux*|haiku*|midipix*|gnu*) + # + # Platforms where the "native" C compiler is GCC or + # accepts compatible command-line arguments, and the + # "native" linker is the GNU linker or accepts + # compatible command-line arguments. + # + RPATH="-Wl,-rpath,\${libdir}" + ;; + solaris*) + # + # Sun/Oracle's linker, the GNU linker, and + # GNU-compatible linkers all support -R. + # + RPATH="-Wl,-R,\${libdir}" + ;; + esac fi # Find a good install program. We prefer a C program (faster), @@ -11129,9 +13895,32 @@ test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' ac_config_headers="$ac_config_headers config.h" + + + + + + + + + + + + + + + + +# +# We're done with configuration operations; add ADDITIONAL_LIBS and +# ADDITIONAL_LIBS_STATIC to LIBS and LIBS_STATIC, respectively. +# +LIBS="$ADDITIONAL_LIBS $LIBS" +LIBS_STATIC="$ADDITIONAL_LIBS_STATIC $LIBS_STATIC" + ac_config_commands="$ac_config_commands default-1" -ac_config_files="$ac_config_files Makefile pcap-filter.manmisc pcap-linktype.manmisc pcap-tstamp.manmisc pcap-savefile.manfile pcap.3pcap pcap_compile.3pcap pcap_datalink.3pcap pcap_dump_open.3pcap pcap_get_tstamp_precision.3pcap pcap_list_datalinks.3pcap pcap_list_tstamp_types.3pcap pcap_open_dead.3pcap pcap_open_offline.3pcap pcap_set_immediate_mode.3pcap pcap_set_tstamp_precision.3pcap pcap_set_tstamp_type.3pcap rpcapd/Makefile rpcapd/rpcapd.manadmin rpcapd/rpcapd-config.manfile testprogs/Makefile" +ac_config_files="$ac_config_files Makefile grammar.y pcap-filter.manmisc pcap-linktype.manmisc pcap-tstamp.manmisc pcap-savefile.manfile pcap.3pcap pcap_compile.3pcap pcap_datalink.3pcap pcap_dump_open.3pcap pcap_get_tstamp_precision.3pcap pcap_list_datalinks.3pcap pcap_list_tstamp_types.3pcap pcap_open_dead.3pcap pcap_open_offline.3pcap pcap_set_immediate_mode.3pcap pcap_set_tstamp_precision.3pcap pcap_set_tstamp_type.3pcap rpcapd/Makefile rpcapd/rpcapd.manadmin rpcapd/rpcapd-config.manfile testprogs/Makefile" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure @@ -11639,7 +14428,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by pcap $as_me 1.9.1, which was +This file was extended by pcap $as_me 1.10.3, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -11705,7 +14494,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -pcap config.status 1.9.1 +pcap config.status 1.10.3 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" @@ -11835,6 +14624,7 @@ do "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; "default-1") CONFIG_COMMANDS="$CONFIG_COMMANDS default-1" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "grammar.y") CONFIG_FILES="$CONFIG_FILES grammar.y" ;; "pcap-filter.manmisc") CONFIG_FILES="$CONFIG_FILES pcap-filter.manmisc" ;; "pcap-linktype.manmisc") CONFIG_FILES="$CONFIG_FILES pcap-linktype.manmisc" ;; "pcap-tstamp.manmisc") CONFIG_FILES="$CONFIG_FILES pcap-tstamp.manmisc" ;; @@ -12413,7 +15203,7 @@ $as_echo "$as_me: executing $ac_file commands" >&6;} "default-1":C) if test -f .devel; then echo timestamp > stamp-h cat $srcdir/Makefile-devel-adds >> Makefile - make depend + make depend || exit 1 fi ;; esac diff --git a/configure.ac b/configure.ac index eba2723954a6..ff7e49066fb7 100644 --- a/configure.ac +++ b/configure.ac @@ -8,7 +8,7 @@ dnl # # See # -# http://ftp.gnu.org/gnu/config/README +# https://ftp.gnu.org/gnu/config/README # # for the URLs to use to fetch new versions of config.guess and # config.sub. @@ -20,20 +20,145 @@ AC_INIT(pcap, m4_esyscmd_s([cat VERSION])) AC_CONFIG_SRCDIR(pcap.c) AC_SUBST(PACKAGE_NAME) +# +# These are the variables that are used in Makefile, pcap-config, and +# libpcap.pc. +# +# CFLAGS: inherited from the environment, not modified by us (except +# temporarily during tests that involve compilation). Used only when +# compiling C source. +# +# CXXFLAGS: inherited from the environment, not modified by us. Used only +# when compiling C++ source. +# +# LDFLAGS: inherited from the environment, not modified by us. +# +# LIBS: inherited from the environment; we add libraries required by +# libpcap. Librares that the core libpcap code requires are added +# first; libraries required by additional pcap modules are first +# added to ADDITIONAL_LIBS, and only added to LIBS at the end, after +# we're finished doing configuration tests for the modules. +# +# LIBS_STATIC: libraries with which a program using the libpcap *static* +# library needs to be linked. This is a superset of LIBS, used in +# pcap-config, so that "pcap-config --libs --static" will report them. +# Initialized to LIBS. +# +# REQUIRES_PRIVATE: pkg-config package names for additional libraries +# with which a program using the libpcap *static* library needs to be +# linked and for which a .pc file exists. This is used in libpcap.pc, +# so that "pkg-config --libs --static" will report them, and so that +# those libraries will be determined using the library's .pc file, not +# from our .pc file. Initialized to an empty string. +# +# V_CCOPT: additional compiler flags other than -I and -D flags +# needed when compiling libpcap. Used in Makefile for both C and +# C++ source. +# +# V_DEFS: additional -D compiler flags needed when compiling +# libpcap. Used in Makefile for both C and C++ source. +# +# V_INCLS: additional -I compiler flags needed when compiling +# libpcap. Used in Makefile for both C and C++ source. +# +# ADDITIONAL_LIBS: additional libraries with which the libpcap dynamic +# library needs to be linked. Used in Makwfile; not used in pcap-config +# or libpcap.pc, as, in all platforms on which we run, if a dynamic +# library is linked with other dynamic libraries, a program using +# that dynamic library doesn't have to link with those libraries - +# they will be automatically loaded at run time. Initialized to an +# empty string. +# +# ADDITIONAL_LIBS_STATIC: additional libraries with which a program +# using the libpcap *static* library needs to be linked. This is used +# in pcap-config, so that "pcap-config --libs --static" will report +# them. Initialized to an empty string. +# +# REQUIRES_PRIVATE: pkg-config package names for additional libraries +# with which a program using the libpcap *static* library needs to be +# linked and for which a .pc file exists. This is used in libpcap.pc, +# so that "pkg-config --libs --static" will report them, and so that +# those libraries will be determined using the library's .pc file, not +# from our .pc file. Initialized to an empty string. +# +# LIBS_PRIVATE: pkg-config package names for additional libraries with +# which a program using the libpcap *static* library needs to be linked +# and for which a .pc file does not exist. This is used in libpcap.pc, +# so that "pkg-config --libs --static" will report them (those libraries +# cannot be determined using the library's .pc file, as there is no such +# file, so it has to come from our .pc file. Initialized to an empty +# string. +# +LIBS_STATIC="" +REQUIRES_PRIVATE="" +LIBS_PRIVATE="" + +AC_SUBST(V_CCOPT) +AC_SUBST(V_DEFS) +AC_SUBST(V_INCLS) +AC_SUBST(LIBS_STATIC) +AC_SUBST(REQUIRES_PRIVATE) +AC_SUBST(LIBS_PRIVATE) + AC_CANONICAL_SYSTEM AC_LBL_C_INIT_BEFORE_CC(V_CCOPT, V_INCLS) # -# Try to enable as many C99 features as we can. -# At minimum, we want C++/C99-style // comments. +# We require C99 or later. +# Try to get it, which may involve adding compiler flags; +# if that fails, give up. # AC_PROG_CC_C99 if test "$ac_cv_prog_cc_c99" = "no"; then - AC_MSG_WARN([The C compiler does not support C99; there may be compiler errors]) + AC_MSG_ERROR([The C compiler does not support C99]) fi + +# +# Get the size of a void *, to determine whether this is a 32-bit +# or 64-bit build. +# +AC_CHECK_SIZEOF([void *]) +ac_lbl_c_sizeof_void_p="$ac_cv_sizeof_void_p" + +# +# We only need a C++ compiler for Haiku; all code except for its +# pcap module is in C. +# +case "$host_os" in +haiku*) + AC_PROG_CXX + + # + # Make sure C and C++ have the same pointer sizes with the flags + # they're given; if they don't, it means that the compilers for the + # languages will, with those flags, not produce code that can be + # linked together. + # + # We have to use different data types, because the results of + # a test are cached, so if we test for the size of a given type + # in C, the subsequent test in C++ will use the cached variable. + # We trick autoconf by testing the size of a "void *" in C and a + # "const void *" in C++. + # + AC_LANG_PUSH([C++]) + AC_CHECK_SIZEOF([const void *]) + ac_lbl_cxx_sizeof_void_p="$ac_cv_sizeof_const_void_p" + AC_LANG_POP([C++]) + if test "$ac_lbl_cxx_sizeof_void_p" -eq 0; then + AC_MSG_ERROR([No C++ compiler was found]) + fi + if test "$ac_lbl_c_sizeof_void_p" -ne "$ac_lbl_cxx_sizeof_void_p"; then + AC_MSG_ERROR([C compiler $CC produces code with $ac_lbl_c_sizeof_void_p-byte pointers +while C++ compiler $CXX produces code with $ac_lbl_cxx_sizeof_void_p-byte pointers. This prevents +code in those languages from being combined.]) + fi + ;; +esac + AC_LBL_C_INIT(V_CCOPT, V_INCLS) AC_LBL_SHLIBS_INIT AC_LBL_C_INLINE +AC_PCAP_C___ATOMICS # # Try to arrange for large file support. @@ -50,41 +175,25 @@ dnl in "aclocal.m4" uses it, so we would still have to test for it dnl and set "HAVE_SYS_IOCCOM_H" if we have it, otherwise dnl "AC_LBL_FIXINCLUDES" wouldn't work on some platforms such as Solaris. dnl -AC_CHECK_HEADERS(sys/ioccom.h sys/sockio.h limits.h) +AC_CHECK_HEADERS(sys/ioccom.h sys/sockio.h) AC_CHECK_HEADERS(netpacket/packet.h) -AC_CHECK_HEADERS(net/pfvar.h, , , [#include -#include -#include ]) -if test "$ac_cv_header_net_pfvar_h" = yes; then - # - # Check for various PF actions. - # - AC_MSG_CHECKING(whether net/pfvar.h defines PF_NAT through PF_NORDR) - AC_TRY_COMPILE( - [#include - #include - #include - #include ], - [return PF_NAT+PF_NONAT+PF_BINAT+PF_NOBINAT+PF_RDR+PF_NORDR;], - [ - AC_MSG_RESULT(yes) - AC_DEFINE(HAVE_PF_NAT_THROUGH_PF_NORDR, 1, - [define if net/pfvar.h defines PF_NAT through PF_NORDR]) - ], - AC_MSG_RESULT(no)) -fi +AC_LBL_SAVE_CHECK_STATE case "$host_os" in -linux*|uclinux*) - AC_CHECK_HEADERS(linux/sockios.h linux/if_bonding.h,,, - [ -#include -#include - ]) +haiku*) + # + # Haiku needs _BSD_SOURCE for the _IO* macros because it doesn't use them. + # + CFLAGS="$CFLAGS -D_BSD_SOURCE" + # + # Haiku has getpass in libbsd. + # + AC_CHECK_LIB(bsd, getpass) ;; esac AC_LBL_FIXINCLUDES +AC_LBL_RESTORE_CHECK_STATE AC_CHECK_FUNCS(strerror) AC_CHECK_FUNC(strerror_r, @@ -124,9 +233,9 @@ main(void) ], [ # - # We don't have strerror_r; do we have strerror_s? + # We don't have strerror_r; do we have _wcserror_s? # - AC_CHECK_FUNCS(strerror_s) + AC_CHECK_FUNCS(_wcserror_s) ]) # @@ -135,45 +244,17 @@ main(void) AC_CHECK_FUNCS(vsyslog) # -# Either: +# Make sure we have vsnprintf() and snprintf(); we require them. # -# we have snprintf() and vsnprintf(), and have asprintf() and -# vasprintf(); -# -# we have snprintf() and vsnprintf(), but don't have asprintf() -# or vasprintf(); -# -# we have neither snprintf() nor vsnprintf(), and don't have -# asprintf() or vasprintf(), either. -# -# We assume that if we have asprintf() we have vasprintf(), as well -# as snprintf() and vsnprintf(), and that if we have snprintf() we -# have vsnprintf(). -# -# For the first case, we don't need any replacement routines. -# For the second case, we need replacement asprintf()/vasprintf() -# routines. -# For the third case, we need replacement snprintf()/vsnprintf() and -# asprintf()/vasprintf() routines. -# -needsnprintf=no -AC_CHECK_FUNCS(vsnprintf snprintf,, - [needsnprintf=yes]) +AC_CHECK_FUNC(vsnprintf,, + AC_MSG_ERROR([vsnprintf() is required but wasn't found])) +AC_CHECK_FUNC(snprintf,, + AC_MSG_ERROR([snprintf() is required but wasn't found])) + needasprintf=no AC_CHECK_FUNCS(vasprintf asprintf,, [needasprintf=yes]) -if test $needsnprintf = yes; then - # - # We assume we have none of them; missing/snprintf.c supplies - # all of them. - # - AC_LIBOBJ([snprintf]) -elif test $needasprintf = yes; then - # - # We assume we have snprintf()/vsnprintf() but lack - # asprintf()/vasprintf(); missing/asprintf.c supplies - # the latter (using vsnprintf()). - # +if test $needasprintf = yes; then AC_LIBOBJ([asprintf]) fi @@ -455,7 +536,7 @@ if test "$ac_cv_func_ether_hostton" = yes; then # This test fails if we don't have # (if we have ether_hostton(), we should have # networking, and if we have networking, we should - # have ) or if we do but it doesn't + # have ) or if we do but it doesn't # declare ether_hostton(). # # Unset ac_cv_have_decl_ether_hostton so we don't @@ -590,7 +671,7 @@ AC_CHECK_HEADER(pthread.h, dnl to pacify those who hate protochain insn AC_MSG_CHECKING(if --disable-protochain option is specified) AC_ARG_ENABLE(protochain, -AC_HELP_STRING([--disable-protochain],[disable \"protochain\" insn])) +AS_HELP_STRING([--disable-protochain],[disable \"protochain\" insn])) case "x$enable_protochain" in xyes) enable_protochain=enabled ;; xno) enable_protochain=disabled ;; @@ -609,22 +690,8 @@ AC_MSG_RESULT(${enable_protochain}) # VALGRINDTEST_SRC= -# -# SITA support is mutually exclusive with native capture support; -# "--with-sita" selects SITA support. -# -AC_ARG_WITH(sita, -AC_HELP_STRING([--with-sita],[include SITA support]), -[ - if test ! "x$withval" = "xno" ; then - AC_DEFINE(SITA,1,[include ACN support]) - AC_MSG_NOTICE(Enabling SITA ACN support) - V_PCAP=sita - fi -], -[ AC_ARG_WITH(pcap, -AC_HELP_STRING([--with-pcap=TYPE],[use packet capture TYPE])) +AS_HELP_STRING([--with-pcap=TYPE],[use packet capture TYPE])) if test ! -z "$with_pcap" ; then V_PCAP="$withval" else @@ -688,6 +755,7 @@ else AC_CHECK_HEADERS(net/pfilt.h net/enet.h) AC_CHECK_HEADERS(net/nit.h sys/net/nit.h) AC_CHECK_HEADERS(linux/socket.h net/raw.h sys/dlpi.h) + AC_CHECK_HEADERS(config/HaikuConfig.h) if test "$ac_cv_lbl_bpf_h_defines_biocsetif" = yes; then # @@ -713,11 +781,6 @@ else # No prizes for guessing this one. # V_PCAP=linux - - # - # XXX - this won't work with older kernels that have - # SOCK_PACKET sockets but not PF_PACKET sockets. - # VALGRINDTEST_SRC=valgrindtest.c elif test "$ac_cv_header_net_pfilt_h" = yes; then # @@ -749,13 +812,18 @@ else # DLPI on pre-Solaris 11 SunOS 5, HP-UX, possibly others. # V_PCAP=dlpi + elif test "$ac_cv_header_config_HaikuConfig_h" = yes; then + # + # Haiku. + # + V_PCAP=haiku else # # Nothing we support. # V_PCAP=null AC_MSG_WARN(cannot determine packet capture interface) - AC_MSG_WARN((see the INSTALL doc for more info)) + AC_MSG_WARN((see the INSTALL.md file for more info)) fi fi AC_MSG_CHECKING(packet capture type) @@ -763,15 +831,77 @@ AC_MSG_RESULT($V_PCAP) AC_SUBST(VALGRINDTEST_SRC) # -# Do capture-mechanism-dependent tests. +# Do we have pkg-config? +# +PKG_PROG_PKG_CONFIG + +# +# Do we have the brew command from Homebrew? +# +AC_PATH_PROG([BREW], [brew]) + +# +# Solaris pkg-config is annoying. For at least one package (D-Bus, I'm +# looking at *you*!), there are separate include files for 32-bit and +# 64-bit builds (I guess using "unsigned long long" as a 64-bit integer +# type on a 64-bit build is like crossing the beams or soething), and +# there are two separate .pc files, so if we're doing a 32-bit build we +# should make sure we look in /usr/lib/pkgconfig for .pc files and if +# we're doing a 64-bit build we should make sure we look in +# /usr/lib/amd64/pkgconfig for .pc files. +# +case "$host_os" in + +solaris*) + if test "$ac_cv_sizeof_void_p" -eq 8; then + # + # 64-bit build. If the path is empty, set it to + # /usr/lib/amd64/pkgconfig; otherwise, if + # /usr/lib/pkgconfig appears in the path, prepend + # /usr/lib/amd64/pkgconfig to it; otherwise, put + # /usr/lib/amd64/pkgconfig at the end. + # + if test -z "$PKG_CONFIG_PATH"; then + # + # Not set, or empty. Set it to + # /usr/lib/amd64/pkgconfig. + # + PKG_CONFIG_PATH=/usr/lib/amd64/pkgconfig + elif test ! -z `echo "$PKG_CONFIG_PATH" | grep "/usr/lib/pkgconfig"`; then + # + # It contains /usr/lib/pkgconfig. Prepend + # /usr/lib/amd64/pkgconfig to /usr/lib/pkgconfig. + # + PKG_CONFIG_PATH=`echo "$PKG_CONFIG_PATH" | sed "s;/usr/lib/pkgconfig;/usr/lib/amd64/pkgconfig:/usr/lib/pkgconfig;"` + else + # + # Not empty, but doesn't contain /usr/lib/pkgconfig. + # Append /usr/lib/amd64/pkgconfig to it. + # + PKG_CONFIG_PATH="$PKG_CONFIG_PATH:/usr/lib/amd64/pkgconfig" + fi + export PKG_CONFIG_PATH + elif test "$ac_cv_sizeof_void_p" -eq 4; then + # + # 32-bit build. If /usr/amd64/lib/pkgconfig appears + # in the path, prepend /usr/lib/pkgconfig to it. + # + if test ! -z `echo "$PKG_CONFIG_PATH" | grep "/usr/lib/amd64/pkgconfig"`; then + # + # It contains /usr/lib/amd64/pkgconfig. Prepend + # /usr/lib/pkgconfig to /usr/lib/amd64/pkgconfig. + # + PKG_CONFIG_PATH=`echo "$PKG_CONFIG_PATH" | sed "s;/usr/lib/amd64/pkgconfig;/usr/lib/pkgconfig:/usr/lib/amd64/pkgconfig;"` + export PKG_CONFIG_PATH + fi + fi +esac + +# +# Handle each capture type. # case "$V_PCAP" in dlpi) - # - # Needed for common functions used by pcap-[dlpi,libdlpi].c - # - SSRC="dlpisubs.c" - # # Checks for some header files. # @@ -788,16 +918,32 @@ dlpi) # Also, due to the bug above applications that link to libpcap with # libdlpi will have to add "-L/lib" option to "configure". # - saved_ldflags=$LDFLAGS + save_LDFLAGS="$LDFLAGS" LDFLAGS="$LIBS -L/lib" AC_CHECK_LIB(dlpi, dlpi_walk, [ LIBS="-ldlpi $LIBS" + LIBS_STATIC="-ldlpi $LIBS_STATIC" + LIBS_PRIVATE="-ldlpi $LIBS_PRIVATE" V_PCAP=libdlpi + + # + # Capture module plus common code needed for + # common functions used by pcap-[dlpi,libdlpi].c + # + PLATFORM_C_SRC="pcap-libdlpi.c dlpisubs.c" AC_DEFINE(HAVE_LIBDLPI,1,[if libdlpi exists]) ], - V_PCAP=dlpi) - LDFLAGS=$saved_ldflags + [ + V_PCAP=dlpi + + # + # Capture module plus common code needed for + # common functions used by pcap-[dlpi,libdlpi].c + # + PLATFORM_C_SRC="pcap-dlpi.c dlpisubs.c" + ]) + LDFLAGS="$save_LDFLAGS" # # Checks whether is usable, to catch weird SCO @@ -832,7 +978,31 @@ dlpi) ]) ;; +enet) + # + # Capture module + # + PLATFORM_C_SRC="pcap-enet.c" + ;; + +haiku) + # + # Capture module + # + PLATFORM_CXX_SRC="pcap-haiku.cpp" + + # + # Just for the sake of it. + # + AC_CHECK_HEADERS(net/if.h net/if_dl.h net/if_types.h) + ;; + linux) + # + # Capture module + # + PLATFORM_C_SRC="pcap-linux.c" + # # Do we have the wireless extensions? # @@ -845,107 +1015,74 @@ linux) # # Do we have libnl? + # We only want version 3. Version 2 was, apparently, + # short-lived, and version 1 is source and binary + # incompatible with version 3, and it appears that, + # these days, everybody's using version 3. We're + # not supporting older versions of the Linux kernel; + # let's drop support for older versions of libnl, too. # AC_ARG_WITH(libnl, - AC_HELP_STRING([--without-libnl],[disable libnl support @<:@default=yes, on Linux, if present@:>@]), + AS_HELP_STRING([--without-libnl],[disable libnl support @<:@default=yes, on Linux, if present@:>@]), with_libnl=$withval,with_libnl=if_available) if test x$with_libnl != xno ; then - have_any_nl="no" - - incdir=-I/usr/include/libnl3 - libnldir= - case "$with_libnl" in - - yes|if_available) - ;; - - *) - if test -d $withval; then - libnldir=-L${withval}/lib/.libs - incdir=-I${withval}/include - fi - ;; - esac - # - # Try libnl 3.x first. + # Check for libnl-genl-3.0 with pkg-config. # - AC_CHECK_LIB(nl-3, nl_socket_alloc, - [ - # - # Yes, we have libnl 3.x. - # - LIBS="${libnldir} -lnl-genl-3 -lnl-3 $LIBS" + PKG_CHECK_MODULES(LIBNL, libnl-genl-3.0, + [ + pkg_config_found_libnl=yes + V_INCLS="$V_INCLS $LIBNL_CFLAGS" + ADDITIONAL_LIBS="$LIBNL_LIBS $ADDITIONAL_LIBS" + ADDITIONAL_LIBS_STATIC="$LIBNL_LIBS_STATIC $ADDITIONAL_LIBS_STATIC" + REQUIRES_PRIVATE="libnl-genl-3.0 $REQUIRES_PRIVATE" AC_DEFINE(HAVE_LIBNL,1,[if libnl exists]) - AC_DEFINE(HAVE_LIBNL_3_x,1,[if libnl exists and is version 3.x]) - AC_DEFINE(HAVE_LIBNL_NLE,1,[libnl has NLE_FAILURE]) - AC_DEFINE(HAVE_LIBNL_SOCKETS,1,[libnl has new-style socket api]) - V_INCLS="$V_INCLS ${incdir}" - have_any_nl="yes" - ],[], ${incdir} ${libnldir} -lnl-genl-3 -lnl-3 ) + ]) - if test x$have_any_nl = xno ; then + if test x$pkg_config_found_libnl != xyes; then # - # Try libnl 2.x + # OK, either we don't have pkg-config or there + # wasn't a .pc file for it; Check for it directly. # - AC_CHECK_LIB(nl, nl_socket_alloc, + case "$with_libnl" in + + yes|if_available) + incdir=-I/usr/include/libnl3 + libnldir= + ;; + + *) + if test -d $withval; then + libnldir=-L${withval}/lib + incdir=-I${withval}/include + fi + ;; + esac + + AC_CHECK_LIB(nl-3, nl_socket_alloc, [ # - # Yes, we have libnl 2.x. + # Yes, we have libnl 3.x. # - LIBS="${libnldir} -lnl-genl -lnl $LIBS" + ADDITIONAL_LIBS="${libnldir} -lnl-genl-3 -lnl-3 $ADDITIONAL_LIBS" + ADDITIONAL_LIBS_STATIC="${libnldir} -lnl-genl-3 -lnl-3 $ADDITIONAL_LIBS_STATIC" + LIBS_PRIVATE="${libnldir} -lnl-genl-3 -lnl-3 $LIBS_PRIVATE" AC_DEFINE(HAVE_LIBNL,1,[if libnl exists]) - AC_DEFINE(HAVE_LIBNL_2_x,1,[if libnl exists and is version 2.x]) - AC_DEFINE(HAVE_LIBNL_NLE,1,[libnl has NLE_FAILURE]) - AC_DEFINE(HAVE_LIBNL_SOCKETS,1,[libnl has new-style socket api]) - have_any_nl="yes" - ]) - fi - - if test x$have_any_nl = xno ; then - # - # No, we don't; do we have libnl 1.x? - # - AC_CHECK_LIB(nl, nl_handle_alloc, - [ + V_INCLS="$V_INCLS ${incdir}" + ],[ # - # Yes. + # No, we don't have libnl at all. + # Fail if the user explicitly requested + # it. # - LIBS="${libnldir} -lnl $LIBS" - AC_DEFINE(HAVE_LIBNL,1,[if libnl exists]) - have_any_nl="yes" - ]) - fi - - if test x$have_any_nl = xno ; then - # - # No, we don't have libnl at all. - # - if test x$with_libnl = xyes ; then - AC_MSG_ERROR([libnl support requested but libnl not found]) - fi + if test x$with_libnl = xyes ; then + AC_MSG_ERROR([libnl support requested but libnl not found]) + fi + ], ${incdir} ${libnldir} -lnl-genl-3 -lnl-3 ) fi fi - AC_CHECK_HEADERS(linux/ethtool.h,,, - [ -AC_INCLUDES_DEFAULT -#include - ]) - - # - # Check to see if struct tpacket_stats is defined in - # . If so, then pcap-linux.c can use this - # to report proper statistics. - # - # -Scott Barron - # - AC_CHECK_TYPES(struct tpacket_stats,,, - [ - #include - ]) - # # Check to see if the tpacket_auxdata struct has a tp_vlan_tci member. # @@ -962,6 +1099,11 @@ AC_INCLUDES_DEFAULT ;; bpf) + # + # Capture module + # + PLATFORM_C_SRC="pcap-bpf.c" + # # Check whether we have the *BSD-style ioctls. # @@ -981,12 +1123,44 @@ bpf) ]) ;; +pf) + # + # Capture module + # + PLATFORM_C_SRC="pcap-pf.c" + ;; + +snit) + # + # Capture module + # + PLATFORM_C_SRC="pcap-snit.c" + ;; + +snoop) + # + # Capture module + # + PLATFORM_C_SRC="pcap-snoop.c" + ;; + dag) # # --with-pcap=dag is the only way to get here, and it means # "DAG support but nothing else" # V_DEFS="$V_DEFS -DDAG_ONLY" + PLATFORM_C_SRC="pcap-dag.c" + xxx_only=yes + ;; + +dpdk) + # + # --with-pcap=dpdk is the only way to get here, and it means + # "DPDK support but nothing else" + # + V_DEFS="$V_DEFS -DDPDK_ONLY" + PLATFORM_C_SRC="pcap-dpdk.c" xxx_only=yes ;; @@ -996,6 +1170,7 @@ septel) # "Septel support but nothing else" # V_DEFS="$V_DEFS -DSEPTEL_ONLY" + PLATFORM_C_SRC="pcap-septel.c" xxx_only=yes ;; @@ -1005,10 +1180,15 @@ snf) # "SNF support but nothing else" # V_DEFS="$V_DEFS -DSNF_ONLY" + PLATFORM_C_SRC="pcap-snf.c" xxx_only=yes ;; null) + # + # Capture module + # + PLATFORM_C_SRC="pcap-null.c" ;; *) @@ -1033,7 +1213,7 @@ then # We have the header, so we use "getifaddrs()" to # get the list of interfaces. # - V_FINDALLDEVS=fad-getad.c + PLATFORM_C_SRC="$PLATFORM_C_SRC fad-getad.c" ],[ # # We don't have the header - give up. @@ -1077,13 +1257,12 @@ then ac_cv_lbl_have_siocglifconf=no)) AC_MSG_RESULT($ac_cv_lbl_have_siocglifconf) if test $ac_cv_lbl_have_siocglifconf = yes ; then - V_FINDALLDEVS=fad-glifc.c + PLATFORM_C_SRC="$PLATFORM_C_SRC fad-glifc.c" else - V_FINDALLDEVS=fad-gifc.c + PLATFORM_C_SRC="$PLATFORM_C_SRC fad-gifc.c" fi ]) fi -]) dnl check for hardware timestamp support case "$host_os" in @@ -1095,15 +1274,6 @@ linux*) ;; esac -AC_ARG_ENABLE([packet-ring], -[AC_HELP_STRING([--enable-packet-ring],[enable packet ring support on Linux @<:@default=yes@:>@])], -,enable_packet_ring=yes) - -if test "x$enable_packet_ring" != "xno" ; then - AC_DEFINE(PCAP_SUPPORT_PACKET_RING, 1, [use packet ring capture support on Linux if available]) - AC_SUBST(PCAP_SUPPORT_PACKET_RING) -fi - # # Check for socklen_t. # @@ -1114,7 +1284,7 @@ AC_CHECK_TYPES(socklen_t,,, ]) AC_ARG_ENABLE(ipv6, -AC_HELP_STRING([--enable-ipv6],[build IPv6-capable version @<:@default=yes@:>@]), +AS_HELP_STRING([--enable-ipv6],[build IPv6-capable version @<:@default=yes@:>@]), [], [enable_ipv6=yes]) if test "$enable_ipv6" != "no"; then @@ -1127,7 +1297,7 @@ fi # Check for Endace DAG card support. AC_ARG_WITH([dag], -AC_HELP_STRING([--with-dag@<:@=DIR@:>@],[include Endace DAG support (located in directory DIR, if supplied). @<:@default=yes, if present@:>@]), +AS_HELP_STRING([--with-dag@<:@=DIR@:>@],[include Endace DAG support (located in directory DIR, if supplied). @<:@default=yes, if present@:>@]), [ if test "$withval" = no then @@ -1160,7 +1330,7 @@ AC_HELP_STRING([--with-dag@<:@=DIR@:>@],[include Endace DAG support (located in ]) AC_ARG_WITH([dag-includes], -AC_HELP_STRING([--with-dag-includes=IDIR],[Endace DAG include directory, if not DIR/include]), +AS_HELP_STRING([--with-dag-includes=IDIR],[Endace DAG include directory, if not DIR/include]), [ # User wants DAG support and has specified a header directory, so use the provided value. want_dag=yes @@ -1168,7 +1338,7 @@ AC_HELP_STRING([--with-dag-includes=IDIR],[Endace DAG include directory, if not ],[]) AC_ARG_WITH([dag-libraries], -AC_HELP_STRING([--with-dag-libraries=LDIR],[Endace DAG library directory, if not DIR/lib]), +AS_HELP_STRING([--with-dag-libraries=LDIR],[Endace DAG library directory, if not DIR/lib]), [ # User wants DAG support and has specified a library directory, so use the provided value. want_dag=yes @@ -1188,57 +1358,77 @@ if test "$want_dag" != no; then if test -z "$dag_lib_dir"; then dag_lib_dir="$dag_root/lib" + # + # Handle multiarch systems. + # + if test -d "$dag_lib_dir/$host" + then + dag_lib_dir="$dag_lib_dir/$host" + fi fi - V_INCLS="$V_INCLS -I$dag_include_dir" - + AC_LBL_SAVE_CHECK_STATE + CFLAGS="$CFLAGS -I$dag_include_dir" AC_CHECK_HEADERS([dagapi.h]) + AC_LBL_RESTORE_CHECK_STATE if test "$ac_cv_header_dagapi_h" = yes; then + V_INCLS="$V_INCLS -I$dag_include_dir" + if test $V_PCAP != dag ; then - SSRC="$SSRC pcap-dag.c" + MODULE_C_SRC="$MODULE_C_SRC pcap-dag.c" fi # Check for various DAG API functions. # Don't need to save and restore LIBS to prevent -ldag being # included if there's a found-action (arg 3). - saved_ldflags=$LDFLAGS + AC_LBL_SAVE_CHECK_STATE LDFLAGS="-L$dag_lib_dir" AC_CHECK_LIB([dag], [dag_attach_stream], - [], + [ + # + # We assume that if we have libdag we have + # libdagconf, as they're installed at the + # same time from the same package. + # + ADDITIONAL_LIBS="-L$dag_lib_dir $ADDITIONAL_LIBS -ldag -ldagconf" + ADDITIONAL_LIBS_STATIC="-L$dag_lib_dir $ADDITIONAL_LIBS_STATIC -ldag -ldagconf" + LIBS_PRIVATE="-L$dag_lib_dir $LIBS_PRIVATE -ldag -ldagconf" + ], [AC_MSG_ERROR(DAG library lacks streams support)]) AC_CHECK_LIB([dag], [dag_attach_stream64], [dag_large_streams="1"], [dag_large_streams="0"]) AC_CHECK_LIB([dag],[dag_get_erf_types], [ AC_DEFINE(HAVE_DAG_GET_ERF_TYPES, 1, [define if you have dag_get_erf_types()])]) AC_CHECK_LIB([dag],[dag_get_stream_erf_types], [ AC_DEFINE(HAVE_DAG_GET_STREAM_ERF_TYPES, 1, [define if you have dag_get_stream_erf_types()])]) - - LDFLAGS=$saved_ldflags + AC_LBL_RESTORE_CHECK_STATE # # We assume that if we have libdag we have libdagconf, # as they're installed at the same time from the same # package. # - LIBS="$LIBS -ldag -ldagconf" - LDFLAGS="$LDFLAGS -L$dag_lib_dir" - if test "$dag_large_streams" = 1; then AC_DEFINE(HAVE_DAG_LARGE_STREAMS_API, 1, [define if you have large streams capable DAG API]) + AC_LBL_SAVE_CHECK_STATE + LIBS="$LIBS -ldag -ldagconf" + LDFLAGS="$LDFLAGS -L$dag_lib_dir" AC_CHECK_LIB([vdag],[vdag_set_device_info], [ac_dag_have_vdag="1"], [ac_dag_have_vdag="0"]) + AC_LBL_RESTORE_CHECK_STATE if test "$ac_dag_have_vdag" = 1; then AC_DEFINE(HAVE_DAG_VDAG, 1, [define if you have vdag_set_device_info()]) if test "$ac_lbl_have_pthreads" != "found"; then AC_MSG_ERROR([DAG requires pthreads, but we didn't find them]) fi - LIBS="$LIBS $PTHREAD_LIBS" + ADDITIONAL_LIBS="$ADDITIONAL_LIBS $PTHREAD_LIBS" + ADDITIONAL_LIBS_STATIC="$ADDITIONAL_LIBS_STATIC $PTHREAD_LIBS" + LIBS_PRIVATE="$LIBS_PRIVATE $PTHREAD_LIBS" fi fi AC_DEFINE(HAVE_DAG_API, 1, [define if you have the DAG API]) else - if test "$V_PCAP" = dag; then # User requested "dag" capture type but we couldn't # find the DAG API support. @@ -1246,14 +1436,15 @@ if test "$want_dag" != no; then fi if test "$want_dag" = yes; then - # User wanted DAG support but we couldn't find it. + # User wanted DAG support but we couldn't find it. AC_MSG_ERROR([DAG support requested with --with-dag, but the DAG headers weren't found at $dag_include_dir: make sure the DAG support is installed, specify a different path or paths if necessary, or don't request DAG support]) fi fi + CFLAGS="$save_CFLAGS" fi AC_ARG_WITH(septel, -AC_HELP_STRING([--with-septel@<:@=DIR@:>@],[include Septel support (located in directory DIR, if supplied). @<:@default=yes, if present@:>@]), +AS_HELP_STRING([--with-septel@<:@=DIR@:>@],[include Septel support (located in directory DIR, if supplied). @<:@default=yes, if present@:>@]), [ if test "$withval" = no then @@ -1308,7 +1499,7 @@ if test "$with_septel" != no; then ADDLARCHIVEOBJS="$ADDLARCHIVEOBJS $septel_tools_dir/asciibin.o $septel_tools_dir/bit2byte.o $septel_tools_dir/confirm.o $septel_tools_dir/fmtmsg.o $septel_tools_dir/gct_unix.o $septel_tools_dir/hqueue.o $septel_tools_dir/ident.o $septel_tools_dir/mem.o $septel_tools_dir/pack.o $septel_tools_dir/parse.o $septel_tools_dir/pool.o $septel_tools_dir/sdlsig.o $septel_tools_dir/strtonum.o $septel_tools_dir/timer.o $septel_tools_dir/trace.o" if test "$V_PCAP" != septel ; then - SSRC="$SSRC pcap-septel.c" + MODULE_C_SRC="$MODULE_C_SRC pcap-septel.c" fi AC_DEFINE(HAVE_SEPTEL_API, 1, [define if you have the Septel API]) @@ -1322,7 +1513,7 @@ if test "$with_septel" != no; then fi if test "$want_septel" = yes; then - # User wanted Septel support but we couldn't find it. + # User wanted Septel support but we couldn't find it. AC_MSG_ERROR([Septel support requested with --with-septel, but the Septel headers weren't found at $septel_include_dir: make sure the Septel support is installed, specify a different path or paths if necessary, or don't request Septel support]) fi fi @@ -1330,7 +1521,7 @@ fi # Check for Myricom SNF support. AC_ARG_WITH([snf], -AC_HELP_STRING([--with-snf@<:@=DIR@:>@],[include Myricom SNF support (located in directory DIR, if supplied). @<:@default=yes, if present@:>@]), +AS_HELP_STRING([--with-snf@<:@=DIR@:>@],[include Myricom SNF support (located in directory DIR, if supplied). @<:@default=yes, if present@:>@]), [ if test "$withval" = no then @@ -1363,7 +1554,7 @@ AC_HELP_STRING([--with-snf@<:@=DIR@:>@],[include Myricom SNF support (located in ]) AC_ARG_WITH([snf-includes], -AC_HELP_STRING([--with-snf-includes=IDIR],[Myricom SNF include directory, if not DIR/include]), +AS_HELP_STRING([--with-snf-includes=IDIR],[Myricom SNF include directory, if not DIR/include]), [ # User wants SNF with specific header directory want_snf=yes @@ -1371,7 +1562,7 @@ AC_HELP_STRING([--with-snf-includes=IDIR],[Myricom SNF include directory, if not ],[]) AC_ARG_WITH([snf-libraries], -AC_HELP_STRING([--with-snf-libraries=LDIR],[Myricom SNF library directory, if not DIR/lib]), +AS_HELP_STRING([--with-snf-libraries=LDIR],[Myricom SNF library directory, if not DIR/lib]), [ # User wants SNF with specific lib directory want_snf=yes @@ -1394,14 +1585,21 @@ if test "$with_snf" != no; then if test -z "$snf_lib_dir"; then snf_lib_dir="$snf_root/lib" + # + # Handle multiarch systems. + # + if test -d "$snf_lib_dir/$host" + then + snf_lib_dir="$snf_lib_dir/$host" + fi fi if test -f "$snf_include_dir/snf.h"; then # We found a header; make sure we can link with the library - saved_ldflags=$LDFLAGS + AC_LBL_SAVE_CHECK_STATE LDFLAGS="$LDFLAGS -L$snf_lib_dir" AC_CHECK_LIB([snf], [snf_init], [ac_cv_lbl_snf_api="yes"]) - LDFLAGS="$saved_ldflags" + AC_LBL_RESTORE_CHECK_STATE if test "$ac_cv_lbl_snf_api" = no; then AC_MSG_ERROR(SNF API cannot correctly be linked; check config.log) fi @@ -1411,11 +1609,12 @@ if test "$with_snf" != no; then AC_MSG_RESULT([yes ($snf_root)]) V_INCLS="$V_INCLS -I$snf_include_dir" - LIBS="$LIBS -lsnf" - LDFLAGS="$LDFLAGS -L$snf_lib_dir" + ADDITIONAL_LIBS="$ADDITIONAL_LIBS -L$snf_lib_dir -lsnf" + ADDITIONAL_LIBS_STATIC="$ADDITIONAL_LIBS_STATIC -L$snf_lib_dir -lsnf" + LIBS_PRIVATE="$LIBS_PRIVATE -L$snf_lib_dir -lsnf" if test "$V_PCAP" != snf ; then - SSRC="$SSRC pcap-snf.c" + MODULE_C_SRC="$MODULE_C_SRC pcap-snf.c" fi AC_DEFINE(HAVE_SNF_API, 1, [define if you have the Myricom SNF API]) @@ -1436,7 +1635,7 @@ fi # Check for Riverbed TurboCap support. AC_ARG_WITH([turbocap], -AC_HELP_STRING([--with-turbocap@<:@=DIR@:>@],[include Riverbed TurboCap support (located in directory DIR, if supplied). @<:@default=yes, if present@:>@]), +AS_HELP_STRING([--with-turbocap@<:@=DIR@:>@],[include Riverbed TurboCap support (located in directory DIR, if supplied). @<:@default=yes, if present@:>@]), [ if test "$withval" = no then @@ -1469,12 +1668,12 @@ if test "$want_turbocap" != no; then AC_MSG_CHECKING(whether TurboCap is supported) - save_CFLAGS="$CFLAGS" - save_LIBS="$LIBS" + AC_LBL_SAVE_CHECK_STATE if test ! -z "$turbocap_root"; then TURBOCAP_CFLAGS="-I$turbocap_root/include" - TURBOCAP_LIBS="-L$turbocap_root/lib" + TURBOCAP_LDFLAGS="-L$turbocap_root/lib" CFLAGS="$CFLAGS $TURBOCAP_CFLAGS" + LDFLAGS="$LDFLAGS $TURBOCAP_LDFLAGS" fi AC_TRY_COMPILE( @@ -1488,20 +1687,22 @@ if test "$want_turbocap" != no; then ], ac_cv_lbl_turbocap_api=yes) - CFLAGS="$save_CFLAGS" + AC_LBL_RESTORE_CHECK_STATE if test $ac_cv_lbl_turbocap_api = yes; then AC_MSG_RESULT(yes) - SSRC="$SSRC pcap-tc.c" + MODULE_C_SRC="$MODULE_C_SRC pcap-tc.c" V_INCLS="$V_INCLS $TURBOCAP_CFLAGS" - LIBS="$LIBS $TURBOCAP_LIBS -lTcApi -lpthread -lstdc++" + ADDITIONAL_LIBS="$ADDITIONAL_LIBS $TURBOCAP_LDFLAGS -lTcApi -lpthread -lstdc++" + ADDITIONAL_LIBS_STATIC="$ADDITIONAL_LIBS_STATIC $TURBOCAP_LDFLAGS -lTcApi -lpthread -lstdc++" + LIBS_PRIVATE="$LIBS_PRIVATE $TURBOCAP_LDFLAGS -lTcApi -lpthread -lstdc++" AC_DEFINE(HAVE_TC_API, 1, [define if you have the TurboCap API]) else AC_MSG_RESULT(no) if test "$want_turbocap" = yes; then - # User wanted Turbo support but we couldn't find it. + # User wanted Turbo support but we couldn't find it. AC_MSG_ERROR([TurboCap support requested with --with-turbocap, but the TurboCap headers weren't found: make sure the TurboCap support is installed or don't request TurboCap support]) fi fi @@ -1513,10 +1714,11 @@ dnl It's off by default, as that increases the attack surface of dnl libpcap, exposing it to malicious servers. dnl AC_MSG_CHECKING([whether to enable remote packet capture]) -AC_ARG_ENABLE(remote, -[ --enable-remote enable remote packet capture @<:@default=no@:>@ - --disable-remote disable remote packet capture],, - enableval=no) +AC_ARG_ENABLE([remote], + [AS_HELP_STRING([--enable-remote], + [enable remote packet capture @<:@default=no@:>@])], + [], + [enableval=no]) case "$enableval" in yes) AC_MSG_RESULT(yes) AC_WARN([Remote packet capture may expose libpcap-based applications]) @@ -1567,9 +1769,165 @@ yes) AC_MSG_RESULT(yes) #include ]) + # + # Optionally, we may want to support SSL. + # Check for OpenSSL/libressl. + # + # First, try looking for it with pkg-config, if we have it. + # + # Homebrew's pkg-config does not, by default, look for + # pkg-config files for packages it has installed. + # Furthermore, at least for OpenSSL, they appear to be + # dumped in package-specific directories whose paths are + # not only package-specific but package-version-specific. + # + # So the only way to find openssl is to get the value of + # PKG_CONFIG_PATH from "brew --env openssl" and add that + # to PKG_CONFIG_PATH. (No, we can't just assume it's under + # /usr/local; Homebrew have conveniently chosen to put it + # under /opt/homebrew on ARM.) + # + # That's the nice thing about Homebrew - it makes things easier! + # Thanks! + # + save_PKG_CONFIG_PATH="$PKG_CONFIG_PATH" + if test -n "$BREW"; then + openssl_pkgconfig_dir=`$BREW --env --plain openssl | sed -n 's/PKG_CONFIG_PATH: //p'` + PKG_CONFIG_PATH="$openssl_pkgconfig_dir:$PKG_CONFIG_PATH" + fi + PKG_CHECK_MODULES(OPENSSL, openssl, + [ + # + # We found OpenSSL/libressl. + # + HAVE_OPENSSL=yes + REQUIRES_PRIVATE="$REQUIRES_PRIVATE openssl" + ]) + PKG_CONFIG_PATH="$save_PKG_CONFIG_PATH" + + # + # If it wasn't found, and we have Homebrew installed, see + # if it's in Homebrew. + # + if test "x$HAVE_OPENSSL" != "xyes" -a -n "$BREW"; then + AC_MSG_CHECKING(for openssl in Homebrew) + # + # The brew man page lies when it speaks of + # $BREW --prefix --installed + # outputting nothing. In Homebrew 3.3.16, + # it produces output regardless of whether + # the formula is installed or not, so we + # send the standard output and error to + # the bit bucket. + # + if $BREW --prefix --installed openssl >/dev/null 2>&1; then + # + # Yes. Get the include directory and library + # directory. (No, we can't just assume it's + # under /usr/local; Homebrew have conveniently + # chosen to put it under /opt/homebrew on ARM.) + # + AC_MSG_RESULT(yes) + HAVE_OPENSSL=yes + openssl_path=`$BREW --prefix openssl` + OPENSSL_CFLAGS="-I$openssl_path/include" + OPENSSL_LIBS="-L$openssl_path/lib -lssl -lcrypto" + OPENSSL_LIBS_STATIC="-L$openssl_path/lib -lssl -lcrypto" + OPENSSL_LIBS_PRIVATE="-L$openssl_path/lib -lssl -lcrypto" + else + AC_MSG_RESULT(no) + fi + fi + + # + # If it wasn't found, and /usr/local/include and /usr/local/lib + # exist, check if it's in /usr/local. (We check whether they + # exist because, if they don't exist, the compiler will warn + # about that and then ignore the argument, so they test + # using just the system header files and libraries.) + # + # We include the standard include file to 1) make sure that + # it's installed (if it's just a shared library for the + # benefit of existing programs, that's not useful) and 2) + # because SSL_library_init() is a library routine in some + # versions and a #defined wrapper around OPENSSL_init_ssl() + # in others. + # + if test "x$HAVE_OPENSSL" != "xyes" -a -d "/usr/local/include" -a -d "/usr/local/lib"; then + AC_LBL_SAVE_CHECK_STATE + CFLAGS="$CFLAGS -I/usr/local/include" + LIBS="$LIBS -L/usr/local/lib -lssl -lcrypto" + AC_MSG_CHECKING(whether we have OpenSSL/libressl in /usr/local that we can use) + AC_TRY_LINK( + [ +#include + ], + [ +SSL_library_init(); +return 0; + ], + [ + AC_MSG_RESULT(yes) + HAVE_OPENSSL=yes + OPENSSL_CFLAGS="-I/usr/local/include" + OPENSSL_LIBS="-L/usr/local/lib -lssl -lcrypto" + OPENSSL_LIBS_STATIC="-L/usr/local/lib -lssl -lcrypto" + OPENSSL_LIBS_PRIVATE="-L/usr/local/lib -lssl -lcrypto" + ], + AC_MSG_RESULT(no)) + AC_LBL_RESTORE_CHECK_STATE + fi + + # + # If it wasn't found, check if it's a system library. + # + # We include the standard include file to 1) make sure that + # it's installed (if it's just a shared library for the + # benefit of existing programs, that's not useful) and 2) + # because SSL_library_init() is a library routine in some + # versions and a #defined wrapper around OPENSSL_init_ssl() + # in others. + # + if test "x$HAVE_OPENSSL" != "xyes"; then + AC_LBL_SAVE_CHECK_STATE + LIBS="$LIBS -lssl -lcrypto" + AC_MSG_CHECKING(whether we have a system OpenSSL/libressl that we can use) + AC_TRY_LINK( + [ +#include + ], + [ +SSL_library_init(); +return 0; + ], + [ + AC_MSG_RESULT(yes) + HAVE_OPENSSL=yes + OPENSSL_LIBS="-lssl -lcrypto" + OPENSSL_LIBS_STATIC="-lssl -lcrypto" + OPENSSL_LIBS_PRIVATE="-lssl -lcrypto" + ], + AC_MSG_RESULT(no)) + AC_LBL_RESTORE_CHECK_STATE + fi + + # + # OK, did we find it? + # + if test "x$HAVE_OPENSSL" = "xyes"; then + AC_DEFINE([HAVE_OPENSSL], [1], [Use OpenSSL]) + V_INCLS="$V_INCLS $OPENSSL_CFLAGS" + ADDITIONAL_LIBS="$ADDITIONAL_LIBS $OPENSSL_LIBS" + ADDITIONAL_LIBS_STATIC="$ADDITIONAL_LIBS_STATIC $OPENSSL_LIBS_STATIC" + LIBS_PRIVATE="$LIBS_PRIVATE $OPENSSL_LIBS_PRIVATE" + REQUIRES_PRIVATE="$REQUIRES_PRIVATE $OPENSSL_REQUIRES_PRIVATE" + else + AC_MSG_NOTICE(OpenSSL not found) + fi + AC_DEFINE(ENABLE_REMOTE,, [Define to 1 if remote packet capture is to be supported]) - SSRC="$SSRC pcap-new.c pcap-rpcap.c rpcap-protocol.c sockutils.c" + REMOTE_C_SRC="$REMOTE_C_SRC pcap-new.c pcap-rpcap.c rpcap-protocol.c sockutils.c sslutils.c" BUILD_RPCAPD=build-rpcapd INSTALL_RPCAPD=install-rpcapd ;; @@ -1579,7 +1937,7 @@ esac AC_MSG_CHECKING(whether to build optimizer debugging code) AC_ARG_ENABLE(optimizer-dbg, -AC_HELP_STRING([--enable-optimizer-dbg],[build optimizer debugging code])) +AS_HELP_STRING([--enable-optimizer-dbg],[build optimizer debugging code])) if test "$enable_optimizer_dbg" = "yes"; then AC_DEFINE(BDEBUG,1,[Enable optimizer debugging]) fi @@ -1587,7 +1945,7 @@ AC_MSG_RESULT(${enable_optimizer_dbg-no}) AC_MSG_CHECKING(whether to build parser debugging code) AC_ARG_ENABLE(yydebug, -AC_HELP_STRING([--enable-yydebug],[build parser debugging code])) +AS_HELP_STRING([--enable-yydebug],[build parser debugging code])) if test "$enable_yydebug" = "yes"; then AC_DEFINE(YYDEBUG,1,[Enable parser debugging]) fi @@ -1613,29 +1971,91 @@ AC_CACHE_CHECK([for capable lex], tcpdump_cv_capable_lex, fi) if test $tcpdump_cv_capable_lex = insufficient ; then AC_MSG_ERROR([$LEX is insufficient to compile libpcap. - libpcap requires Flex 2.5.31 or later, or a compatible version of lex.]) + libpcap requires Flex 2.5.31 or later, or a compatible version of lex. + If a suitable version of Lex/Flex is available as a non-standard command + and/or not in the PATH, you can specify it using the LEX environment + variable. That said, on some systems the error can mean that Flex/Lex is + actually acceptable, but m4 is not. Likewise, if a suitable version of + m4 (such as GNU M4) is available but has not been detected, you can + specify it using the M4 environment variable.]) fi # # Look for yacc/bison/byacc. +# If it's Bison, we do not want -y, as 1) we will be using -o to cause +# the output for XXX.y to be written to XXX.c and 2) we don't want +# it to issue warnings about stuff not supported by POSIX YACC - we +# want to use that stuff, and don't care whether plain YACC supports +# it or not, we require either Bison or Berkeley YACC. # -AC_PROG_YACC - +BISON_BYACC="" # -# Make sure it supports the -p flag and supports processing our -# grammar.y. +# Look for Bison. # -AC_CACHE_CHECK([for capable yacc/bison], tcpdump_cv_capable_yacc, - if $YACC -p pcap_ -o /dev/null $srcdir/grammar.y >/dev/null 2>&1; then - tcpdump_cv_capable_yacc=yes +AC_CHECK_PROGS(BISON_BYACC, bison) +if test x"$BISON_BYACC" != x; then + # + # We found Bison. + # + # Bison prior to 2.4(.1) doesn't support "%define api.pure", so use + # "%pure-parser". + # + bison_major_version=`$BISON_BYACC -V | sed -n 's/.* \(@<:@1-9@:>@@<:@0-9@:>@*\)\.@<:@0-9@:>@@<:@0-9.@:>@*/\1/p'` + bison_minor_version=`$BISON_BYACC -V | sed -n 's/.* @<:@1-9@:>@@<:@0-9@:>@*\.\(@<:@0-9@:>@+\).*/\1/p'` + if test "$bison_major_version" -lt 2 -o \ + \( "$bison_major_version" -eq 2 -a "$bison_major_version" -lt 4 \) + then + REENTRANT_PARSER="%pure-parser" else - tcpdump_cv_capable_yacc=insufficient - fi) -if test $tcpdump_cv_capable_yacc = insufficient ; then - AC_MSG_ERROR([$YACC is insufficient to compile libpcap. + REENTRANT_PARSER="%define api.pure" + fi +else + # + # We didn't find Bison; check for Berkeley YACC, under the + # names byacc and yacc. + # + AC_CHECK_PROGS(BISON_BYACC, byacc yacc) + if test x"$BISON_BYACC" != x; then + # + # Make sure this is Berkeley YACC, not AT&T YACC; + # the latter doesn't support reentrant parsers. + # Run it with "-V"; that succeeds and reports the + # version number with Berkeley YACC, but will + # (probably) fail with various vendor flavors + # of AT&T YACC. + # + # Hopefully this also eliminates any versions + # of Berkeley YACC that don't support reentrant + # parsers, if there are any. + # + AC_CACHE_CHECK([for capable yacc], tcpdump_cv_capable_yacc, + if $BISON_BYACC -V >/dev/null 2>&1; then + tcpdump_cv_capable_yacc=yes + else + tcpdump_cv_capable_yacc=insufficient + fi) + if test $tcpdump_cv_capable_yacc = insufficient ; then + AC_MSG_ERROR([$BISON_BYACC is insufficient to compile libpcap. libpcap requires Bison, a newer version of Berkeley YACC with support for reentrant parsers, or another YACC compatible with them.]) + fi + else + # + # OK, we found neither byacc nor yacc. + # + AC_MSG_ERROR([Neither bison, byacc, nor yacc was found. + libpcap requires Bison, a newer version of Berkeley YACC with support + for reentrant parsers, or another YACC compatible with them.]) + fi + + # + # Berkeley YACC doesn't support "%define api.pure", so use + # "%pure-parser". + # + REENTRANT_PARSER="%pure-parser" fi +AC_SUBST(BISON_BYACC) +AC_SUBST(REENTRANT_PARSER) # # Do various checks for various OSes and versions of those OSes. @@ -1698,7 +2118,7 @@ darwin*) DYEXT="dylib" V_CCOPT="$V_CCOPT -fno-common" AC_ARG_ENABLE(universal, - AC_HELP_STRING([--disable-universal],[don't build universal on macOS])) + AS_HELP_STRING([--disable-universal],[don't build universal on macOS])) if test "$enable_universal" != "no"; then case "$host_os" in @@ -1728,7 +2148,7 @@ darwin*) V_PROG_LDFLAGS_FAT="-arch ppc -arch ppc64" ;; - darwin8.[[456]]|darwin.[[456]].*) + darwin8.[[456]]|darwin8.[[456]].*) # # Tiger, subsequent to Intel support but prior # to x86-64 support. Build libraries and @@ -1793,33 +2213,41 @@ darwin*) V_PROG_LDFLAGS_FAT="-arch x86_64 -arch i386" ;; - darwin*) + darwin1[[1-8]]*) # - # Post-Snow Leopard. Build libraries for x86-64 - # and 32-bit x86, with x86-64 first, and build - # executables only for x86-64. (That's what - # Apple does.) This requires no special flags - # for programs. - # XXX - update if and when Apple drops support - # for 32-bit x86 code and if and when Apple adds - # ARM-based Macs. (You're on your own for iOS - # etc.) + # Post-Snow Leopard, pre-Catalina. Build + # libraries for x86-64 and 32-bit x86, with + # x86-64 first, and build executables only for + # x86-64. (That's what Apple does.) This + # requires no special flags for programs. # - # XXX - check whether we *can* build for - # i386 and, if not, suggest that the user - # install the /usr/include headers if they - # want to build fat. + # We check whether we *can* build for i386 and, + # if not, suggest that the user install the + # /usr/include headers if they want to build + # fat. # AC_MSG_CHECKING(whether building for 32-bit x86 is supported) - save_CFLAGS="$CFLAGS" + AC_LBL_SAVE_CHECK_STATE CFLAGS="$CFLAGS -arch i386" - AC_TRY_COMPILE( + AC_TRY_LINK( [], [return 0;], [ AC_MSG_RESULT(yes) - V_LIB_CCOPT_FAT="-arch x86_64 -arch i386" - V_LIB_LDFLAGS_FAT="-arch x86_64 -arch i386" + V_LIB_CCOPT_FAT="-arch x86_64" + V_LIB_LDFLAGS_FAT="-arch x86_64" + + # + # OpenSSL installation on macOS seems + # to install only the libs for 64-bit + # x86 - at least that's what Brew does: + # only configure 32-bit builds if we + # don't have OpenSSL. + # + if test "$HAVE_OPENSSL" != yes; then + V_LIB_CCOPT_FAT="$V_LIB_CCOPT_FAT -arch i386" + V_LIB_LDFLAGS_FAT="$V_LIB_LDFLAGS_FAT -arch i386" + fi ], [ AC_MSG_RESULT(no) @@ -1846,7 +2274,67 @@ darwin*) ;; esac ]) - CFLAGS="$save_CFLAGS" + AC_LBL_RESTORE_CHECK_STATE + ;; + + darwin19*) + # + # Catalina. Build libraries and executables + # only for x86-64. (That's what Apple does; + # 32-bit x86 binaries are not supported on + # Catalina.) + # + V_LIB_CCOPT_FAT="-arch x86_64" + V_LIB_LDFLAGS_FAT="-arch x86_64" + V_PROG_CCOPT_FAT="-arch x86_64" + V_PROG_LDFLAGS_FAT="-arch x86_64" + ;; + + darwin*) + # + # Post-Catalina. Build libraries and + # executables for x86-64 and ARM64. + # (That's what Apple does, except they + # build for arm64e, which may include + # some of the pointer-checking extensions.) + # + # If we're building with libssl, make sure + # we can build fat with it (i.e., that it + # was built fat); if we can't, don't set + # the target architectures, and just + # build for the host we're on. + # + # Otherwise, just add both of them. + # + if test "$HAVE_OPENSSL" = yes; then + AC_MSG_CHECKING(whether building fat with libssl is supported) + AC_LBL_SAVE_CHECK_STATE + CFLAGS="$CFLAGS -arch x86_64 -arch arm64" + LDFLAGS="$LDFLAGS $OPENSSL_LIBS" + AC_TRY_LINK( + [ + #include + ], + [ + SSL_library_init(); + return 0; + ], + [ + AC_MSG_RESULT(yes) + V_LIB_CCOPT_FAT="-arch x86_64 -arch arm64" + V_LIB_LDFLAGS_FAT="-arch x86_64 -arch arm64" + V_PROG_CCOPT_FAT="-arch x86_64 -arch arm64" + V_PROG_LDFLAGS_FAT="-arch x86_64 -arch arm64" + ], + [AC_MSG_RESULT(no)] + ) + AC_LBL_RESTORE_CHECK_STATE + else + V_LIB_CCOPT_FAT="-arch x86_64 -arch arm64" + V_LIB_LDFLAGS_FAT="-arch x86_64 -arch arm64" + V_PROG_CCOPT_FAT="-arch x86_64 -arch arm64" + V_PROG_LDFLAGS_FAT="-arch x86_64 -arch arm64" + fi ;; esac fi @@ -1920,23 +2408,15 @@ irix*) MAN_MISC_INFO=5 ;; -linux*|freebsd*|netbsd*|openbsd*|dragonfly*|kfreebsd*|gnu*|midipix*) +linux*|freebsd*|netbsd*|openbsd*|dragonfly*|kfreebsd*|gnu*|haiku*|midipix*) DYEXT="so" - - # - # Compiler assumed to be GCC; run-time linker may require a -R - # flag. - # - if test "$libdir" != "/usr/lib"; then - V_RFLAGS=-Wl,-R$libdir - fi ;; osf*) DYEXT="so" # - # DEC OSF/1, a/k/a Digial UNIX, a/k/a Tru64 UNIX. + # DEC OSF/1, a/k/a Digital UNIX, a/k/a Tru64 UNIX. # Use Tru64 UNIX conventions for man pages; they're the same as # the System V conventions except that they use section 8 for # administrative commands and daemons. @@ -1991,9 +2471,18 @@ solaris*) esac ;; esac +AC_SUBST(V_LIB_CCOPT_FAT) +AC_SUBST(V_LIB_LDFLAGS_FAT) +AC_SUBST(V_PROG_CCOPT_FAT) +AC_SUBST(V_PROG_LDFLAGS_FAT) +AC_SUBST(DYEXT) +AC_SUBST(MAN_DEVICES) +AC_SUBST(MAN_FILE_FORMATS) +AC_SUBST(MAN_MISC_INFO) +AC_SUBST(MAN_ADMIN_COMMANDS) AC_ARG_ENABLE(shared, -AC_HELP_STRING([--enable-shared],[build shared libraries @<:@default=yes, if support available@:>@])) +AS_HELP_STRING([--enable-shared],[build shared libraries @<:@default=yes, if support available@:>@])) test "x$enable_shared" = "xno" && DYEXT="none" AC_PROG_RANLIB @@ -2040,126 +2529,82 @@ AC_CHECK_MEMBERS([dl_hp_ppa_info_t.dl_module_id_1],,, #include ]) -AC_LBL_UNALIGNED_ACCESS - -AC_SUBST(V_CCOPT) -AC_SUBST(V_LIB_CCOPT_FAT) -AC_SUBST(V_LIB_LDFLAGS_FAT) -AC_SUBST(V_PROG_CCOPT_FAT) -AC_SUBST(V_PROG_LDFLAGS_FAT) -AC_SUBST(V_DEFS) -AC_SUBST(V_FINDALLDEVS) -AC_SUBST(V_INCLS) -AC_SUBST(V_LEX) -AC_SUBST(V_PCAP) -AC_SUBST(V_SHLIB_CCOPT) -AC_SUBST(V_SHLIB_CMD) -AC_SUBST(V_SHLIB_OPT) -AC_SUBST(V_SONAME_OPT) -AC_SUBST(V_RPATH_OPT) -AC_SUBST(V_YACC) -AC_SUBST(ADDLOBJS) -AC_SUBST(ADDLARCHIVEOBJS) -AC_SUBST(SSRC) -AC_SUBST(DYEXT) -AC_SUBST(MAN_DEVICES) -AC_SUBST(MAN_FILE_FORMATS) -AC_SUBST(MAN_MISC_INFO) -AC_SUBST(MAN_ADMIN_COMMANDS) -AC_SUBST(PTHREAD_LIBS) -AC_SUBST(BUILD_RPCAPD) -AC_SUBST(INSTALL_RPCAPD) -AC_SUBST(RPCAPD_LIBS) -AC_SUBST(EXTRA_NETWORK_LIBS) - +# +# Various Linux-specific mechanisms. +# AC_ARG_ENABLE([usb], -[AC_HELP_STRING([--enable-usb],[enable USB capture support @<:@default=yes, if support available@:>@])], +[AS_HELP_STRING([--enable-usb],[enable Linux usbmon USB capture support @<:@default=yes, if support available@:>@])], [], [enable_usb=yes]) -if test "xxx_only" = yes; then - # User requested something-else-only pcap, so they don't - # want USB support. - enable_usb=no -fi - -if test "x$enable_usb" != "xno" ; then - dnl check for USB sniffing support - AC_MSG_CHECKING(for USB sniffing support) - case "$host_os" in - linux*) - AC_DEFINE(PCAP_SUPPORT_USB, 1, [target host supports USB sniffing]) - USB_SRC=pcap-usb-linux.c - AC_MSG_RESULT(yes) - ac_usb_dev_name=`udevinfo -q name -p /sys/class/usb_device/usbmon 2>/dev/null` - if test $? -ne 0 ; then - ac_usb_dev_name="usbmon" - fi - AC_DEFINE_UNQUOTED(LINUX_USB_MON_DEV, "/dev/$ac_usb_dev_name", [path for device for USB sniffing]) - AC_MSG_NOTICE(Device for USB sniffing is /dev/$ac_usb_dev_name) - # - # Do we have a version of available? - # If so, we might need it for . - # - AC_CHECK_HEADERS(linux/compiler.h) - if test "$ac_cv_header_linux_compiler_h" = yes; then - # - # Yes - include it when testing for . - # - AC_CHECK_HEADERS(linux/usbdevice_fs.h,,,[#include ]) - else - AC_CHECK_HEADERS(linux/usbdevice_fs.h) - fi - if test "$ac_cv_header_linux_usbdevice_fs_h" = yes; then - # - # OK, does it define bRequestType? Older versions of the kernel - # define fields with names like "requesttype, "request", and - # "value", rather than "bRequestType", "bRequest", and - # "wValue". - # - AC_CHECK_MEMBERS([struct usbdevfs_ctrltransfer.bRequestType],,, - [ - AC_INCLUDES_DEFAULT - #ifdef HAVE_LINUX_COMPILER_H - #include - #endif - #include - ]) - fi - ;; - freebsd*) - # - # This just uses BPF in FreeBSD 8.4 and later; we don't need - # to check for anything special for capturing. - # - AC_MSG_RESULT([yes, in FreeBSD 8.4 and later]) - ;; - - *) - AC_MSG_RESULT(no) - ;; -esac -fi -AC_SUBST(PCAP_SUPPORT_USB) -AC_SUBST(USB_SRC) - -dnl check for netfilter sniffing support +# +# If somebody requested an XXX-only pcap, that doesn't include +# additional mechanisms. +# if test "xxx_only" != yes; then - AC_MSG_CHECKING(whether the platform could support netfilter sniffing) - case "$host_os" in - linux*) - AC_MSG_RESULT(yes) - # - # Life's too short to deal with trying to get this to compile - # if you don't get the right types defined with - # __KERNEL_STRICT_NAMES getting defined by some other include. - # - # Check whether the includes Just Work. If not, don't turn on - # netfilter support. - # - AC_MSG_CHECKING(whether we can compile the netfilter support) - AC_CACHE_VAL(ac_cv_netfilter_can_compile, - AC_TRY_COMPILE([ + case "$host_os" in + linux*) + dnl check for USB sniffing support + AC_MSG_CHECKING(for Linux usbmon USB sniffing support) + if test "x$enable_usb" != "xno" ; then + AC_DEFINE(PCAP_SUPPORT_LINUX_USBMON, 1, [target host supports Linux usbmon for USB sniffing]) + MODULE_C_SRC="$MODULE_C_SRC pcap-usb-linux.c" + AC_MSG_RESULT(yes) + # + # Note: if the directory for special files is *EVER* somewhere + # other than the UN*X standard of /dev (which will break any + # software that looks for /dev/null or /dev/tty, for example, + # so doing that is *REALLY* not a good idea), please provide + # some mechanism to determine that directory at *run time*, + # rather than *configure time*, so that it works when doinga + # a cross-build, and that works with *multiple* distributions, + # with our without udev, and with multiple versions of udev, + # with udevinfo or udevadm or any other mechanism to get the + # special files directory. + # + # Do we have a version of available? + # If so, we might need it for . + # + AC_CHECK_HEADERS(linux/compiler.h) + if test "$ac_cv_header_linux_compiler_h" = yes; then + # + # Yes - include it when testing for . + # + AC_CHECK_HEADERS(linux/usbdevice_fs.h,,,[#include ]) + else + AC_CHECK_HEADERS(linux/usbdevice_fs.h) + fi + if test "$ac_cv_header_linux_usbdevice_fs_h" = yes; then + # + # OK, does it define bRequestType? Older versions of the kernel + # define fields with names like "requesttype, "request", and + # "value", rather than "bRequestType", "bRequest", and + # "wValue". + # + AC_CHECK_MEMBERS([struct usbdevfs_ctrltransfer.bRequestType],,, + [ + AC_INCLUDES_DEFAULT + #ifdef HAVE_LINUX_COMPILER_H + #include + #endif + #include + ]) + fi + else + AC_MSG_RESULT(no) + fi + + # + # Life's too short to deal with trying to get this to compile + # if you don't get the right types defined with + # __KERNEL_STRICT_NAMES getting defined by some other include. + # + # Check whether the includes Just Work. If not, don't turn on + # netfilter support. + # + AC_MSG_CHECKING(whether we can compile the netfilter support) + AC_CACHE_VAL(ac_cv_netfilter_can_compile, + AC_TRY_COMPILE([ AC_INCLUDES_DEFAULT #include #include @@ -2170,26 +2615,23 @@ AC_INCLUDES_DEFAULT #include #include #include ], - [], - ac_cv_netfilter_can_compile=yes, - ac_cv_netfilter_can_compile=no)) - AC_MSG_RESULT($ac_cv_netfilter_can_compile) - if test $ac_cv_netfilter_can_compile = yes ; then - AC_DEFINE(PCAP_SUPPORT_NETFILTER, 1, - [target host supports netfilter sniffing]) - NETFILTER_SRC=pcap-netfilter-linux.c - fi - ;; - *) - AC_MSG_RESULT(no) - ;; - esac + [], + ac_cv_netfilter_can_compile=yes, + ac_cv_netfilter_can_compile=no)) + AC_MSG_RESULT($ac_cv_netfilter_can_compile) + if test $ac_cv_netfilter_can_compile = yes ; then + AC_DEFINE(PCAP_SUPPORT_NETFILTER, 1, + [target host supports netfilter sniffing]) + MODULE_C_SRC="$MODULE_C_SRC pcap-netfilter-linux.c" + fi + ;; + esac fi +AC_SUBST(PCAP_SUPPORT_LINUX_USBMON) AC_SUBST(PCAP_SUPPORT_NETFILTER) -AC_SUBST(NETFILTER_SRC) AC_ARG_ENABLE([netmap], -[AC_HELP_STRING([--enable-netmap],[enable netmap support @<:@default=yes, if support available@:>@])], +[AS_HELP_STRING([--enable-netmap],[enable netmap support @<:@default=yes, if support available@:>@])], [], [enable_netmap=yes]) @@ -2213,15 +2655,218 @@ AC_INCLUDES_DEFAULT if test $ac_cv_net_netmap_user_can_compile = yes ; then AC_DEFINE(PCAP_SUPPORT_NETMAP, 1, [target host supports netmap]) - NETMAP_SRC=pcap-netmap.c + MODULE_C_SRC="$MODULE_C_SRC pcap-netmap.c" fi AC_SUBST(PCAP_SUPPORT_NETMAP) - AC_SUBST(NETMAP_SRC) fi +# Check for DPDK support. +AC_ARG_WITH([dpdk], +AS_HELP_STRING([--with-dpdk@<:@=DIR@:>@],[include DPDK support (located in directory DIR, if supplied). @<:@default=yes, if present@:>@]), +[ + if test "$withval" = no + then + # User doesn't want DPDK support. + want_dpdk=no + elif test "$withval" = yes + then + # User wants DPDK support but hasn't specified a directory. + want_dpdk=yes + else + # User wants DPDK support and has specified a directory, + # so use the provided value. + want_dpdk=yes + dpdk_dir=$withval + fi +],[ + if test "$V_PCAP" = dpdk; then + # User requested DPDK-only libpcap, so we'd better have + # the DPDK API. + want_dpdk=yes + elif test "xxx_only" = yes; then + # User requested something-else-only pcap, so they don't + # want DPDK support. + want_dpdk=no + else + # + # Use DPDK API if present, otherwise don't + # + want_dpdk=ifpresent + fi +]) + +if test "$want_dpdk" != no; then + # + # The user didn't explicitly say they don't want DPDK, + # so see if we have it. + # + # We only try to find it using pkg-config; DPDK is *SO* + # complicated - DPDK 19.02, for example, has about 117(!) + # libraries, and the precise set of libraries required has + # changed over time - so attempting to guess which libraries + # you need, and hardcoding that in an attempt to find the + # libraries without DPDK, rather than relying on DPDK to + # tell you, with a .pc file, what libraries are needed, + # is *EXTREMELY* fragile and has caused some bug reports, + # so we're just not going to do it. + # + # If that causes a problem, the only thing we will do is + # accept an alternative way of finding the appropriate + # library set for the installed version of DPDK that is + # as robust as pkg-config (i.e., it had better work as well + # as pkg-config with *ALL* versions of DPDK that provide a + # libdpdk.pc file). + # + # If --with-dpdk={path} was specified, add {path}/pkgconfig + # to PKG_CONFIG_PATH, so we look for the .pc file there, + # first. + # + save_PKG_CONFIG_PATH="$PKG_CONFIG_PATH" + if test -n "$dpdk_dir"; then + PKG_CONFIG_PATH="$dpdk_dir:$PKG_CONFIG_PATH" + fi + PKG_CHECK_MODULES(DPDK, libdpdk, + [ + found_dpdk=yes + ]) + PKG_CONFIG_PATH="$save_PKG_CONFIG_PATH" + + # + # Did we find DPDK? + # + if test "$found_dpdk" = yes; then + # + # Found it. + # + # We call rte_eth_dev_count_avail(), and older versions + # of DPDK didn't have it, so check for it. + # + AC_LBL_SAVE_CHECK_STATE + CFLAGS="$CFLAGS $DPDK_CFLAGS" + LIBS="$LIBS $DPDK_LIBS" + AC_CHECK_FUNC(rte_eth_dev_count_avail) + AC_LBL_RESTORE_CHECK_STATE + fi + + if test "$ac_cv_func_rte_eth_dev_count_avail" = yes; then + # + # We found a usable DPDK. + # + # Check whether the rte_ether.h file defines + # struct ether_addr or struct rte_ether_addr. + # + # ("API compatibility? That's for losers!") + # + AC_LBL_SAVE_CHECK_STATE + CFLAGS="$CFLAGS $DPDK_CFLAGS" + LIBS="$LIBS $DPDK_LIBS" + AC_CHECK_TYPES(struct rte_ether_addr,,, + [ + #include + ]) + AC_LBL_RESTORE_CHECK_STATE + + # + # We can build with DPDK. + # + V_INCLS="$V_INCLS $DPDK_CFLAGS" + ADDITIONAL_LIBS="$ADDITIONAL_LIBS $DPDK_LIBS" + ADDITIONAL_LIBS_STATIC="$ADDITIONAL_LIBS_STATIC $DPDK_LIBS_STATIC" + REQUIRES_PRIVATE="$REQUIRES_PRIVATE libdpdk" + AC_DEFINE(PCAP_SUPPORT_DPDK, 1, [target host supports DPDK]) + if test $V_PCAP != dpdk ; then + MODULE_C_SRC="$MODULE_C_SRC pcap-dpdk.c" + fi + else + # + # We didn't find a usable DPDK. + # If we required it (with --with-dpdk or --with-pcap=dpdk), + # fail with an appropriate message telling the user what + # the problem was, otherwise note the problem with a + # warning. + # + if test "$found_dpdk" != yes; then + # + # Not found with pkg-config. Note that we + # require that DPDK must be findable with + # pkg-config. + # + if test "$V_PCAP" = dpdk; then + # + # User requested DPDK-only capture support. + # + AC_MSG_ERROR( +[DPDK support requested with --with-pcap=dpdk, but +we couldn't find DPDK with pkg-config. Make sure that pkg-config is +installed, that DPDK 18.02.2 or later is installed, and that DPDK +provides a .pc file.]) + fi + + if test "$want_dpdk" = yes; then + # + # User requested that libpcap include + # DPDK capture support. + # + AC_MSG_ERROR( +[DPDK support requested with --with-dpdk, but we +couldn't find DPDK with pkg-config. Make sure that pkg-config +is installed, that DPDK 18.02.2 or later is installed, and that +DPDK provides .pc file.]) + fi + + # + # User didn't indicate whether they wanted DPDK + # or not; just warn why we didn't find it. + # + AC_MSG_WARN( +[We couldn't find DPDK with pkg-config. If +you want DPDK support, make sure that pkg-config is installed, +that DPDK 18.02.2 or later is installed, and that DPDK provides a +.pc file.]) + elif test "$ac_cv_func_rte_eth_dev_count_avail" != yes; then + # + # Found with pkg-config, but we couldn't compile + # a program that calls rte_eth_dev_count(); we + # probably have the developer package installed, + # but don't have a sufficiently recent version + # of DPDK. Note that we need a sufficiently + # recent version of DPDK. + # + if test "$V_PCAP" = dpdk; then + # + # User requested DPDK-only capture support. + # + AC_MSG_ERROR( +[DPDK support requested with --with-pcap=dpdk, but we +can't compile libpcap with DPDK. Make sure that DPDK 18.02.2 or later +is installed.]) + fi + + if test "$want_dpdk" = yes; then + # + # User requested that libpcap include + # DPDK capture support. + # + AC_MSG_ERROR( +[DPDK support requested with --with-dpdk, but +we can't compile libpcap with DPDK. Make sure that DPDK 18.02.2 +or later is DPDK is installed.]) + fi + + # + # User didn't indicate whether they wanted DPDK + # or not; just warn why we didn't find it. + # + AC_MSG_WARN( +[DPDK was found, but we can't compile libpcap with it. +Make sure that DPDK 18.02.2 or later is installed.]) + fi + fi +fi +AC_SUBST(PCAP_SUPPORT_DPDK) AC_ARG_ENABLE([bluetooth], -[AC_HELP_STRING([--enable-bluetooth],[enable Bluetooth support @<:@default=yes, if support available@:>@])], +[AS_HELP_STRING([--enable-bluetooth],[enable Bluetooth support @<:@default=yes, if support available@:>@])], [], [enable_bluetooth=ifsupportavailable]) @@ -2242,7 +2887,7 @@ if test "x$enable_bluetooth" != "xno" ; then # sniffing. # AC_DEFINE(PCAP_SUPPORT_BT, 1, [target host supports Bluetooth sniffing]) - BT_SRC=pcap-bt-linux.c + MODULE_C_SRC="$MODULE_C_SRC pcap-bt-linux.c" AC_MSG_NOTICE(Bluetooth sniffing is supported) ac_lbl_bluetooth_available=yes @@ -2269,7 +2914,7 @@ if test "x$enable_bluetooth" != "xno" ; then AC_MSG_RESULT(yes) AC_DEFINE(PCAP_SUPPORT_BT_MONITOR,, [target host supports Bluetooth Monitor]) - BT_MONITOR_SRC=pcap-bt-monitor-linux.c + MODULE_C_SRC="$MODULE_C_SRC pcap-bt-monitor-linux.c" ], [ AC_MSG_RESULT(no) @@ -2301,12 +2946,10 @@ if test "x$enable_bluetooth" != "xno" ; then ;; esac AC_SUBST(PCAP_SUPPORT_BT) - AC_SUBST(BT_SRC) - AC_SUBST(BT_MONITOR_SRC) fi AC_ARG_ENABLE([dbus], -[AC_HELP_STRING([--enable-dbus],[enable D-Bus capture support @<:@default=yes, if support available@:>@])], +[AS_HELP_STRING([--enable-dbus],[enable D-Bus capture support @<:@default=yes, if support available@:>@])], [], [enable_dbus=ifavailable]) @@ -2349,53 +2992,47 @@ if test "x$enable_dbus" != "xno"; then fi if test "x$enable_dbus" != "xno"; then - AC_CHECK_PROG([PKGCONFIG], [pkg-config], [pkg-config], [no]) - if test "x$PKGCONFIG" != "xno"; then - AC_MSG_CHECKING([for D-Bus]) - if "$PKGCONFIG" dbus-1; then + PKG_CHECK_MODULES(DBUS, dbus-1, + [ + AC_LBL_SAVE_CHECK_STATE + CFLAGS="$CFLAGS $DBUS_CFLAGS" + LIBS="$LIBS $DBUS_LIBS" + AC_MSG_CHECKING(whether the D-Bus library defines dbus_connection_read_write) + AC_TRY_LINK( + [#include + + #include + #include + + #include ], + [return dbus_connection_read_write(NULL, 0);], + [ AC_MSG_RESULT([yes]) - DBUS_CFLAGS=`"$PKGCONFIG" --cflags dbus-1` - DBUS_LIBS=`"$PKGCONFIG" --libs dbus-1` - save_CFLAGS="$CFLAGS" - save_LIBS="$LIBS" - CFLAGS="$CFLAGS $DBUS_CFLAGS" - LIBS="$LIBS $DBUS_LIBS" - AC_MSG_CHECKING(whether the D-Bus library defines dbus_connection_read_write) - AC_TRY_LINK( - [#include - - #include - #include - - #include ], - [return dbus_connection_read_write(NULL, 0);], - [ - AC_MSG_RESULT([yes]) - AC_DEFINE(PCAP_SUPPORT_DBUS, 1, [support D-Bus sniffing]) - DBUS_SRC=pcap-dbus.c - V_INCLS="$V_INCLS $DBUS_CFLAGS" - ], - [ - AC_MSG_RESULT([no]) - if test "x$enable_dbus" = "xyes"; then - AC_MSG_ERROR([--enable-dbus was given, but the D-Bus library doesn't define dbus_connection_read_write()]) - fi - LIBS="$save_LIBS" - ]) - CFLAGS="$save_CFLAGS" - else + AC_DEFINE(PCAP_SUPPORT_DBUS, 1, [support D-Bus sniffing]) + MODULE_C_SRC="$MODULE_C_SRC pcap-dbus.c" + V_INCLS="$V_INCLS $DBUS_CFLAGS" + ADDITIONAL_LIBS="$ADDITIONAL_LIBS $DBUS_LIBS" + ADDITIONAL_LIBS_STATIC="$ADDITIONAL_LIBS_STATIC $DBUS_LIBS_STATIC" + REQUIRES_PRIVATE="$REQUIRES_PRIVATE dbus-1" + ], + [ AC_MSG_RESULT([no]) if test "x$enable_dbus" = "xyes"; then - AC_MSG_ERROR([--enable-dbus was given, but the dbus-1 package is not installed]) + AC_MSG_ERROR([--enable-dbus was given, but the D-Bus library doesn't define dbus_connection_read_write()]) fi + ]) + AC_LBL_RESTORE_CHECK_STATE + ], + [ + if test "x$enable_dbus" = "xyes"; then + AC_MSG_ERROR([--enable-dbus was given, but the dbus-1 package is not installed]) fi - fi + ]) AC_SUBST(PCAP_SUPPORT_DBUS) - AC_SUBST(DBUS_SRC) fi AC_ARG_ENABLE([rdma], -[AC_HELP_STRING([--enable-rdma],[enable RDMA capture support @<:@default=yes, if support available@:>@])], +[AS_HELP_STRING([--enable-rdma],[enable RDMA capture support @<:@default=yes, if support available@:>@])], [], [enable_rdma=ifavailable]) @@ -2406,7 +3043,33 @@ if test "xxx_only" = yes; then fi if test "x$enable_rdma" != "xno"; then - AC_CHECK_LIB(ibverbs, ibv_get_device_list, [ + PKG_CHECK_MODULES(LIBIBVERBS, libibverbs, + [ + found_libibverbs=yes + LIBIBVERBS_REQUIRES_PRIVATE="libibverbs" + ]) + + if test "x$found_libibverbs" != "xyes"; then + AC_CHECK_LIB(ibverbs, ibv_get_device_list, + [ + found_libibverbs=yes + LIBIBVERBS_CFLAGS="" + LIBIBVERBS_LIBS="-libverbs" + # XXX - at least on Ubuntu 20.04, there are many more + # libraries needed; is there any platform where + # libibverbs is available but where pkg-config isn't + # available or libibverbs doesn't use it? If not, + # we should only use pkg-config for it. + LIBIBVERBS_LIBS_STATIC="-libverbs" + LIBIBVERBS_LIBS_PRIVATE="-libverbs" + ] + ) + fi + + if test "x$found_libibverbs" = "xyes"; then + AC_LBL_SAVE_CHECK_STATE + CFLAGS="$CFLAGS $LIBIBVERBS_CFLAGS" + LIBS="$LIBS $LIBIBVERBS_LIBS" AC_CHECK_HEADER(infiniband/verbs.h, [ # # ibv_create_flow may be defined as a static inline @@ -2430,30 +3093,117 @@ if test "x$enable_rdma" != "xno"; then ], [ AC_MSG_RESULT([yes]) - AC_DEFINE(PCAP_SUPPORT_RDMASNIFF, , [target host supports RDMA sniffing]) - RDMA_SRC=pcap-rdmasniff.c - LIBS="-libverbs $LIBS" + found_usable_libibverbs=yes ], [ AC_MSG_RESULT([no]) ] ) ]) - ]) + AC_LBL_RESTORE_CHECK_STATE + fi + + if test "x$found_usable_libibverbs" = "xyes" + then + AC_DEFINE(PCAP_SUPPORT_RDMASNIFF, , [target host supports RDMA sniffing]) + MODULE_C_SRC="$MODULE_C_SRC pcap-rdmasniff.c" + CFLAGS="$LIBIBVERBS_CFLAGS $CFLAGS" + ADDITIONAL_LIBS="$LIBIBVERBS_LIBS $ADDITIONAL_LIBS" + ADDITIONAL_LIBS_STATIC="$LIBIBVERBS_LIBS_STATIC $ADDITIONAL_LIBS_STATIC" + LIBS_PRIVATE="$LIBIBVERBS_LIBS_PRIVATE $LIBS_PRIVATE" + REQUIRES_PRIVATE="$REQUIRES_PRIVATE $LIBIBVERBS_REQUIRES_PRIVATE" + fi AC_SUBST(PCAP_SUPPORT_RDMASNIFF) - AC_SUBST(RDMA_SRC) +fi + +# +# If this is a platform where we need to have the .pc file and +# pcap-config script supply an rpath option to specify the directory +# in which the libpcap shared library is installed, and the install +# prefix /usr (meaning we're not installing a system library), provide +# the rpath option. +# +# (We must check $prefix, as $libdir isn't necessarily /usr/lib in this +# case - for example, Linux distributions for 64-bit platforms that +# also provide support for binaries for a 32-bit version of the +# platform may put the 64-bit libraries, the 32-bit libraries, or both +# in directories other than /usr/lib.) +# +# In AIX, do we have to do this? +# +# In Darwin-based OSes, the full paths of the shared libraries with +# which the program was linked are stored in the executable, so we don't +# need to provide an rpath option. +# +# With the HP-UX linker, directories specified with -L are, by default, +# added to the run-time search path, so we don't need to supply them. +# +# For Tru64 UNIX, "-rpath" works with DEC's^WCompaq's^WHP's C compiler +# for Alpha, but isn't documented as working with GCC, and no GCC- +# compatible option is documented as working with the DEC compiler. +# If anybody needs this on Tru64/Alpha, they're welcome to figure out a +# way to make it work. +# +# This must *not* depend on the compiler, as, on platforms where there's +# a GCC-compatible compiler and a vendor compiler, we need to work with +# both. +# +if test "$prefix" != "/usr"; then + case "$host_os" in + + freebsd*|netbsd*|openbsd*|dragonfly*|linux*|haiku*|midipix*|gnu*) + # + # Platforms where the "native" C compiler is GCC or + # accepts compatible command-line arguments, and the + # "native" linker is the GNU linker or accepts + # compatible command-line arguments. + # + RPATH="-Wl,-rpath,\${libdir}" + ;; + + solaris*) + # + # Sun/Oracle's linker, the GNU linker, and + # GNU-compatible linkers all support -R. + # + RPATH="-Wl,-R,\${libdir}" + ;; + esac fi AC_PROG_INSTALL AC_CONFIG_HEADER(config.h) +AC_SUBST(V_SHLIB_CCOPT) +AC_SUBST(V_SHLIB_CMD) +AC_SUBST(V_SHLIB_OPT) +AC_SUBST(V_SONAME_OPT) +AC_SUBST(RPATH) +AC_SUBST(ADDLOBJS) +AC_SUBST(ADDLARCHIVEOBJS) +AC_SUBST(PLATFORM_C_SRC) +AC_SUBST(PLATFORM_CXX_SRC) +AC_SUBST(MODULE_C_SRC) +AC_SUBST(REMOTE_C_SRC) +AC_SUBST(PTHREAD_LIBS) +AC_SUBST(BUILD_RPCAPD) +AC_SUBST(INSTALL_RPCAPD) +AC_SUBST(RPCAPD_LIBS) + +# +# We're done with configuration operations; add ADDITIONAL_LIBS and +# ADDITIONAL_LIBS_STATIC to LIBS and LIBS_STATIC, respectively. +# +LIBS="$ADDITIONAL_LIBS $LIBS" +LIBS_STATIC="$ADDITIONAL_LIBS_STATIC $LIBS_STATIC" + AC_OUTPUT_COMMANDS([if test -f .devel; then echo timestamp > stamp-h cat $srcdir/Makefile-devel-adds >> Makefile - make depend + make depend || exit 1 fi]) -AC_OUTPUT(Makefile pcap-filter.manmisc pcap-linktype.manmisc +AC_OUTPUT(Makefile grammar.y pcap-filter.manmisc pcap-linktype.manmisc pcap-tstamp.manmisc pcap-savefile.manfile pcap.3pcap pcap_compile.3pcap pcap_datalink.3pcap pcap_dump_open.3pcap pcap_get_tstamp_precision.3pcap pcap_list_datalinks.3pcap diff --git a/diag-control.h b/diag-control.h index cfc581b37b57..ae2641b45969 100644 --- a/diag-control.h +++ b/diag-control.h @@ -37,20 +37,100 @@ #include "pcap/compiler-tests.h" -#ifndef _MSC_VER +#if PCAP_IS_AT_LEAST_CLANG_VERSION(2,8) || PCAP_IS_AT_LEAST_GNUC_VERSION(4,6) /* * Clang and GCC both support this way of putting pragmas into #defines. - * We don't use it unless we have a compiler that supports it; the - * warning-suppressing pragmas differ between Clang and GCC, so we test - * for both of those separately. + * We use it only if we have a compiler that supports it; see below + * for the code that uses it and the #defines that control whether + * that code is used. */ #define PCAP_DO_PRAGMA(x) _Pragma (#x) #endif /* - * Suppress Flex warnings. + * Suppress "enum value not explicitly handled in switch" warnings. + * We may have to build on multiple different Windows SDKs, so we + * may not be able to include all enum values in a switch, as they + * won't necessarily be defined on all the SDKs, and, unlike + * #defines, there's no easy way to test whether a given enum has + * a given value. It *could* be done by the configure script or + * CMake tests. */ #if defined(_MSC_VER) + #define DIAG_OFF_ENUM_SWITCH \ + __pragma(warning(push)) \ + __pragma(warning(disable:4061)) + #define DIAG_ON_ENUM_SWITCH \ + __pragma(warning(pop)) +#else + #define DIAG_OFF_ENUM_SWITCH + #define DIAG_ON_ENUM_SWITCH +#endif + +/* + * Suppress "switch statement has only a default case" warnings. + * There's a switch in bpf_filter.c that only has additional + * cases on Linux. + */ +#if defined(_MSC_VER) + #define DIAG_OFF_DEFAULT_ONLY_SWITCH \ + __pragma(warning(push)) \ + __pragma(warning(disable:4065)) + #define DIAG_ON_DEFAULT_ONLY_SWITCH \ + __pragma(warning(pop)) +#else + #define DIAG_OFF_DEFAULT_ONLY_SWITCH + #define DIAG_ON_DEFAULT_ONLY_SWITCH +#endif + +/* + * Suppress Flex, narrowing, and deprecation warnings. + */ +#if PCAP_IS_AT_LEAST_CLANG_VERSION(2,8) + /* + * This is Clang 2.8 or later; we can use "clang diagnostic + * ignored -Wxxx" and "clang diagnostic push/pop". + * + * Suppress -Wdocumentation warnings; GCC doesn't support -Wdocumentation, + * at least according to the GCC 7.3 documentation. Apparently, Flex + * generates code that upsets at least some versions of Clang's + * -Wdocumentation. + * + * (This could be clang-cl, which defines _MSC_VER, so test this + * before testing _MSC_VER.) + */ + #define DIAG_OFF_FLEX \ + PCAP_DO_PRAGMA(clang diagnostic push) \ + PCAP_DO_PRAGMA(clang diagnostic ignored "-Wsign-compare") \ + PCAP_DO_PRAGMA(clang diagnostic ignored "-Wdocumentation") \ + PCAP_DO_PRAGMA(clang diagnostic ignored "-Wshorten-64-to-32") \ + PCAP_DO_PRAGMA(clang diagnostic ignored "-Wmissing-noreturn") \ + PCAP_DO_PRAGMA(clang diagnostic ignored "-Wunused-parameter") \ + PCAP_DO_PRAGMA(clang diagnostic ignored "-Wunreachable-code") + #define DIAG_ON_FLEX \ + PCAP_DO_PRAGMA(clang diagnostic pop) + + /* + * Suppress the only narrowing warnings you get from Clang. + */ + #define DIAG_OFF_NARROWING \ + PCAP_DO_PRAGMA(clang diagnostic push) \ + PCAP_DO_PRAGMA(clang diagnostic ignored "-Wshorten-64-to-32") + + #define DIAG_ON_NARROWING \ + PCAP_DO_PRAGMA(clang diagnostic pop) + + /* + * Suppress deprecation warnings. + */ + #define DIAG_OFF_DEPRECATION \ + PCAP_DO_PRAGMA(clang diagnostic push) \ + PCAP_DO_PRAGMA(clang diagnostic ignored "-Wdeprecated-declarations") + #define DIAG_ON_DEPRECATION \ + PCAP_DO_PRAGMA(clang diagnostic pop) + #define DIAG_OFF_FORMAT_TRUNCATION + #define DIAG_ON_FORMAT_TRUNCATION +#elif defined(_MSC_VER) /* * This is Microsoft Visual Studio; we can use __pragma(warning(disable:XXXX)) * and __pragma(warning(push/pop)). @@ -64,26 +144,29 @@ __pragma(warning(disable:4242)) \ __pragma(warning(disable:4244)) \ __pragma(warning(disable:4702)) - #define DIAG_ON_FLEX __pragma(warning(pop)) -#elif PCAP_IS_AT_LEAST_CLANG_VERSION(2,8) - /* - * This is Clang 2.8 or later; we can use "clang diagnostic - * ignored -Wxxx" and "clang diagnostic push/pop". - * - * Suppress -Wdocumentation warnings; GCC doesn't support -Wdocumentation, - * at least according to the GCC 7.3 documentation. Apparently, Flex - * generates code that upsets at least some versions of Clang's - * -Wdocumentation. - */ - #define DIAG_OFF_FLEX \ - PCAP_DO_PRAGMA(clang diagnostic push) \ - PCAP_DO_PRAGMA(clang diagnostic ignored "-Wsign-compare") \ - PCAP_DO_PRAGMA(clang diagnostic ignored "-Wdocumentation") \ - PCAP_DO_PRAGMA(clang diagnostic ignored "-Wmissing-noreturn") \ - PCAP_DO_PRAGMA(clang diagnostic ignored "-Wunused-parameter") \ - PCAP_DO_PRAGMA(clang diagnostic ignored "-Wunreachable-code") #define DIAG_ON_FLEX \ - PCAP_DO_PRAGMA(clang diagnostic pop) + __pragma(warning(pop)) + + /* + * Suppress narrowing warnings. + */ + #define DIAG_OFF_NARROWING \ + __pragma(warning(push)) \ + __pragma(warning(disable:4242)) \ + __pragma(warning(disable:4311)) + #define DIAG_ON_NARROWING \ + __pragma(warning(pop)) + + /* + * Suppress deprecation warnings. + */ + #define DIAG_OFF_DEPRECATION \ + __pragma(warning(push)) \ + __pragma(warning(disable:4996)) + #define DIAG_ON_DEPRECATION \ + __pragma(warning(pop)) + #define DIAG_OFF_FORMAT_TRUNCATION + #define DIAG_ON_FORMAT_TRUNCATION #elif PCAP_IS_AT_LEAST_GNUC_VERSION(4,6) /* * This is GCC 4.6 or later, or a compiler claiming to be that. @@ -97,6 +180,37 @@ PCAP_DO_PRAGMA(GCC diagnostic ignored "-Wunreachable-code") #define DIAG_ON_FLEX \ PCAP_DO_PRAGMA(GCC diagnostic pop) + + /* + * GCC currently doesn't issue any narrowing warnings. + */ + #define DIAG_OFF_NARROWING + #define DIAG_ON_NARROWING + + /* + * Suppress deprecation warnings. + */ + #define DIAG_OFF_DEPRECATION \ + PCAP_DO_PRAGMA(GCC diagnostic push) \ + PCAP_DO_PRAGMA(GCC diagnostic ignored "-Wdeprecated-declarations") + #define DIAG_ON_DEPRECATION \ + PCAP_DO_PRAGMA(GCC diagnostic pop) + + /* + * Suppress format-truncation= warnings. + * GCC 7.1 had introduced this warning option. Earlier versions (at least + * one particular copy of GCC 4.6.4) treat the request as a warning. + */ + #if PCAP_IS_AT_LEAST_GNUC_VERSION(7,1) + #define DIAG_OFF_FORMAT_TRUNCATION \ + PCAP_DO_PRAGMA(GCC diagnostic push) \ + PCAP_DO_PRAGMA(GCC diagnostic ignored "-Wformat-truncation=") + #define DIAG_ON_FORMAT_TRUNCATION \ + PCAP_DO_PRAGMA(GCC diagnostic pop) + #else + #define DIAG_OFF_FORMAT_TRUNCATION + #define DIAG_ON_FORMAT_TRUNCATION + #endif #else /* * Neither Visual Studio, nor Clang 2.8 or later, nor GCC 4.6 or later @@ -105,6 +219,12 @@ */ #define DIAG_OFF_FLEX #define DIAG_ON_FLEX + #define DIAG_OFF_NARROWING + #define DIAG_ON_NARROWING + #define DIAG_OFF_DEPRECATION + #define DIAG_ON_DEPRECATION + #define DIAG_OFF_FORMAT_TRUNCATION + #define DIAG_ON_FORMAT_TRUNCATION #endif #ifdef YYBYACC @@ -124,96 +244,95 @@ * In addition, the generated code may have functions with unreachable * code, so suppress warnings about those. */ - #if defined(_MSC_VER) + #if PCAP_IS_AT_LEAST_CLANG_VERSION(2,8) /* - * This is Microsoft Visual Studio; we can use - * __pragma(warning(disable:XXXX)) and __pragma(warning(push/pop)). + * This is Clang 2.8 or later (including clang-cl, so test this + * before _MSC_VER); we can use "clang diagnostic ignored -Wxxx". */ #define DIAG_OFF_BISON_BYACC \ - __pragma(warning(push)) \ - __pragma(warning(disable:4702)) - #define DIAG_ON_BISON_BYACC __pragma(warning(pop)) - #elif PCAP_IS_AT_LEAST_CLANG_VERSION(2,8) - /* - * This is Clang 2.8 or later; we can use "clang diagnostic - * ignored -Wxxx" and "clang diagnostic push/pop". - */ - #define DIAG_OFF_BISON_BYACC \ - PCAP_DO_PRAGMA(clang diagnostic push) \ PCAP_DO_PRAGMA(clang diagnostic ignored "-Wshadow") \ PCAP_DO_PRAGMA(clang diagnostic ignored "-Wunreachable-code") - #define DIAG_ON_BISON_BYACC \ - PCAP_DO_PRAGMA(clang diagnostic pop) + #elif defined(_MSC_VER) + /* + * This is Microsoft Visual Studio; we can use + * __pragma(warning(disable:XXXX)). + */ + #define DIAG_OFF_BISON_BYACC \ + __pragma(warning(disable:4702)) #elif PCAP_IS_AT_LEAST_GNUC_VERSION(4,6) /* * This is GCC 4.6 or later, or a compiler claiming to be that. - * We can use "GCC diagnostic ignored -Wxxx" (introduced in 4.2) - * and "GCC diagnostic push/pop" (introduced in 4.6). + * We can use "GCC diagnostic ignored -Wxxx" (introduced in 4.2, + * but it may not actually work very well prior to 4.6). */ #define DIAG_OFF_BISON_BYACC \ - PCAP_DO_PRAGMA(GCC diagnostic push) \ PCAP_DO_PRAGMA(GCC diagnostic ignored "-Wshadow") \ PCAP_DO_PRAGMA(GCC diagnostic ignored "-Wunreachable-code") - #define DIAG_ON_BISON_BYACC \ - PCAP_DO_PRAGMA(GCC diagnostic pop) #else /* * Neither Clang 2.8 or later nor GCC 4.6 or later or a compiler * claiming to be that; there's nothing we know of that we can do. */ #define DIAG_OFF_BISON_BYACC - #define DIAG_ON_BISON_BYACC #endif #else /* * Bison. * - * The generated code may have functions with unreachable code, so - * suppress warnings about those. + * The generated code may have functions with unreachable code and + * switches with only a default case, so suppress warnings about those. */ - #if defined(_MSC_VER) + #if PCAP_IS_AT_LEAST_CLANG_VERSION(2,8) + /* + * This is Clang 2.8 or later (including clang-cl, so test this + * before _MSC_VER); we can use "clang diagnostic ignored -Wxxx". + */ + #define DIAG_OFF_BISON_BYACC \ + PCAP_DO_PRAGMA(clang diagnostic ignored "-Wunreachable-code") + #elif defined(_MSC_VER) /* * This is Microsoft Visual Studio; we can use - * __pragma(warning(disable:XXXX)) and __pragma(warning(push/pop)). + * __pragma(warning(disable:XXXX)). * * Suppress some /Wall warnings. */ #define DIAG_OFF_BISON_BYACC \ - __pragma(warning(push)) \ + __pragma(warning(disable:4065)) \ __pragma(warning(disable:4127)) \ __pragma(warning(disable:4242)) \ __pragma(warning(disable:4244)) \ __pragma(warning(disable:4702)) - #define DIAG_ON_BISON_BYACC __pragma(warning(pop)) - #elif PCAP_IS_AT_LEAST_CLANG_VERSION(2,8) - /* - * This is Clang 2.8 or later; we can use "clang diagnostic - * ignored -Wxxx" and "clang diagnostic push/pop". - */ - #define DIAG_OFF_BISON_BYACC \ - PCAP_DO_PRAGMA(clang diagnostic push) \ - PCAP_DO_PRAGMA(clang diagnostic ignored "-Wunreachable-code") - #define DIAG_ON_BISON_BYACC \ - PCAP_DO_PRAGMA(clang diagnostic pop) #elif PCAP_IS_AT_LEAST_GNUC_VERSION(4,6) /* * This is GCC 4.6 or later, or a compiler claiming to be that. - * We can use "GCC diagnostic ignored -Wxxx" (introduced in 4.2) - * and "GCC diagnostic push/pop" (introduced in 4.6). + * We can use "GCC diagnostic ignored -Wxxx" (introduced in 4.2, + * but it may not actually work very well prior to 4.6). */ #define DIAG_OFF_BISON_BYACC \ - PCAP_DO_PRAGMA(GCC diagnostic push) \ PCAP_DO_PRAGMA(GCC diagnostic ignored "-Wunreachable-code") - #define DIAG_ON_BISON_BYACC \ - PCAP_DO_PRAGMA(GCC diagnostic pop) #else /* * Neither Clang 2.8 or later nor GCC 4.6 or later or a compiler * claiming to be that; there's nothing we know of that we can do. */ #define DIAG_OFF_BISON_BYACC - #define DIAG_ON_BISON_BYACC #endif #endif +/* + * GCC needs this on AIX for longjmp(). + */ +#if PCAP_IS_AT_LEAST_GNUC_VERSION(5,1) + /* + * Beware that the effect of this builtin is more than just squelching the + * warning! GCC trusts it enough for the process to segfault if the control + * flow reaches the builtin (an infinite empty loop in the same context would + * squelch the warning and ruin the process too, albeit in a different way). + * So please remember to use this very carefully. + */ + #define PCAP_UNREACHABLE __builtin_unreachable(); +#else + #define PCAP_UNREACHABLE +#endif + #endif /* _diag_control_h */ diff --git a/dlpisubs.c b/dlpisubs.c index 5f6e41af80b5..6815b0ec2cba 100644 --- a/dlpisubs.c +++ b/dlpisubs.c @@ -113,6 +113,20 @@ pcap_stats_dlpi(pcap_t *p, struct pcap_stat *ps) return (0); } +/* + * Does the processor for which we're compiling this support aligned loads? + */ +#if (defined(__i386__) || defined(_M_IX86) || defined(__X86__) || defined(__x86_64__) || defined(_M_X64)) || \ + (defined(__arm__) || defined(_M_ARM) || defined(__aarch64__)) || \ + (defined(__m68k__) && (!defined(__mc68000__) && !defined(__mc68010__))) || \ + (defined(__ppc__) || defined(__ppc64__) || defined(_M_PPC) || defined(_ARCH_PPC) || defined(_ARCH_PPC64)) || \ + (defined(__s390__) || defined(__s390x__) || defined(__zarch__)) + /* Yes, it does. */ +#else + /* No, it doesn't. */ + #define REQUIRE_ALIGNMENT +#endif + /* * Loop through the packets and call the callback for each packet. * Return the number of packets read. @@ -127,12 +141,17 @@ pcap_process_pkts(pcap_t *p, pcap_handler callback, u_char *user, struct pcap_pkthdr pkthdr; #ifdef HAVE_SYS_BUFMOD_H struct sb_hdr *sbp; -#ifdef LBL_ALIGN +#ifdef REQUIRE_ALIGNMENT struct sb_hdr sbhdr; #endif #endif - /* Loop through packets */ + /* + * Loop through packets. + * + * This assumes that a single buffer of packets will have + * <= INT_MAX packets, so the packet count doesn't overflow. + */ ep = bufp + len; n = 0; @@ -157,7 +176,7 @@ pcap_process_pkts(pcap_t *p, pcap_handler callback, u_char *user, return (n); } } -#ifdef LBL_ALIGN +#ifdef REQUIRE_ALIGNMENT if ((long)bufp & 3) { sbp = &sbhdr; memcpy(sbp, bufp, sizeof(*sbp)); @@ -176,7 +195,7 @@ pcap_process_pkts(pcap_t *p, pcap_handler callback, u_char *user, bufp += caplen; #endif ++pd->stat.ps_recv; - if (bpf_filter(p->fcode.bf_insns, pk, origlen, caplen)) { + if (pcap_filter(p->fcode.bf_insns, pk, origlen, caplen)) { #ifdef HAVE_SYS_BUFMOD_H pkthdr.ts.tv_sec = sbp->sbh_timestamp.tv_sec; pkthdr.ts.tv_usec = sbp->sbh_timestamp.tv_usec; @@ -275,7 +294,7 @@ pcap_process_mactype(pcap_t *p, u_int mactype) * XXX - DL_IPNET devices default to "raw IP" rather than * "IPNET header"; see * - * http://seclists.org/tcpdump/2009/q1/202 + * https://seclists.org/tcpdump/2009/q1/202 * * We'd have to do DL_IOC_IPNET_INFO to enable getting * the IPNET header. @@ -286,7 +305,7 @@ pcap_process_mactype(pcap_t *p, u_int mactype) #endif default: - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "unknown mactype 0x%x", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "unknown mactype 0x%x", mactype); retv = -1; } diff --git a/doc/DLT_ALLOCATE_HOWTO.md b/doc/DLT_ALLOCATE_HOWTO.md deleted file mode 100644 index ff77128e2302..000000000000 --- a/doc/DLT_ALLOCATE_HOWTO.md +++ /dev/null @@ -1,29 +0,0 @@ -DLT and LINKTYPE allocation -=========================== - -DLT_ types live in pcap/dlt.h. They can be requested by the community on a -First-Come First-Served basis [i.e. https://tools.ietf.org/html/rfc8126#section-4.4 ] -(Although libpcap is not at this time an IETF specification, there have been -some as yet-incomplete efforts to do this). - -The Tcpdump Group prefers to link to an open specification on the new DLT_ -type, but they are available for closed, proprietary projects as well. -In that case, a stable email address suffices so that someone who finds -an unknown DLT_ type can investigate. -We prefer to give out unambiguous numbers, and we try to do it as quickly -as possible, but DLT_USERx is available while you wait. - -Note that DLT_ types are, in theory, private to the capture mechanism and can -in some cases be operating system specific, and so a second set of values, -LINKTYPE_ is allocated for actually writing to pcap files. As much as -possible going forward, the DLT_ and LINKTYPE_ value are identical, however, -this was not always the case. See pcap-common.c. - -The LINKTYPE_ values are not exported, but are in pcap-common.c only. - -DEVELOPER NOTES ---------------- - -When allocating a new DLT_ value, a corresponding value needs to be -added to pcap-common.c. -It is not necessary to copy the comments from dlt.h to pcap-common.c. diff --git a/doc/README.Win32.md b/doc/README.Win32.md index 8de25c85a95c..018796517ddf 100644 --- a/doc/README.Win32.md +++ b/doc/README.Win32.md @@ -1,3 +1,198 @@ -Win32 used to build with Visual Studio 6, but we now use cmake. +Building libpcap on Windows with Visual Studio +============================================== -This file needs to be adopted by a windows expert developer. +Unlike the UN*Xes on which libpcap can capture network traffic, Windows +has no network traffic capture mechanism that libpcap can use. +Therefore, libpcap requires a driver, and a library to access the +driver, provided by the Npcap or WinPcap projects. + +Those projects include versions of libpcap built to use that driver and +library; these instructions are for people who want to build libpcap +source releases, or libpcap from the Git repository, as a replacement +for the version provided with Npcap or WinPcap. + +Npcap and WinPcap SDK +--------------------- + +In order to build libpcap, you will need to download Npcap and its +software development kit (SDK) or WinPcap and its software development +kit. + +Npcap is currently being developed and maintained, and offers many +additional capabilities that WinPcap does not. + +WinPcap is no longer being developed or maintained; it should be used +only if there is some other requirement to use it rather than Npcap, +such as a requirement to support versions of Windows earlier than +Windows Vista, which is the earliest version supported by Npcap. + +Npcap and its SDK can be downloaded from its home page: + + https://npcap.com + +The SDK is a ZIP archive; create a folder on your C: drive, e.g. +C:\npcap-sdk, and put the contents of the ZIP archive into that folder. + +The WinPcap installer can be downloaded from + + https://www.winpcap.org/install/default.htm + +and the WinPcap Developer's Kit can be downloaded from + + https://www.winpcap.org/devel.htm + +Required build tools +-------------------- + +The Developer's Kit is a ZIP archive; it contains a folder named +WpdPack, which you should place on your C: drive, e.g. C:\WpdPack. + +Building libpcap on Windows requires Visual Studio 2015 or later. The +Community Edition of Visual Studio can be downloaded at no cost from + + https://visualstudio.microsoft.com + +Additional tools are also required. Chocolatey is a package manager for +Windows with which those tools, and other tools, can be installed; it +can be downloaded from + + https://chocolatey.org + +It is a command-line tool; a GUI tool, Chocolatey GUI, is provided as a +Chocolatey package, which can be installed from the command line: + + choco install chocolateygui + +For convenience, the "choco install" command can be run with the "-y" +flag, forcing it to automatically answer all questions asked of the user +with "yes": + + choco install -y chocolateygui + +The required tools are: + +### CMake ### + +libpcap does not provide supported project files for Visual Studio +(there are currently unsupported project files provided, but we do not +guarantee that they will work or that we will continue to provide them). +It does provide files for CMake, which is a cross-platform tool that +runs on UN*Xes and on Windows and that can generate project files for +UN*X Make, the Ninja build system, and Visual Studio, among other build +systems. + +Visual Studio 2015 does not provide CMake; an installer can be +downloaded from + + https://cmake.org/download/ + +When you run the installer, you should choose to add CMake to the system +PATH for all users and to create the desktop icon. + +CMake can also be installed as the Chocolatey package "cmake": + + choco install -y cmake + +Visual Studio 2017 and later provide CMake, so you will not need to +install CMake if you have installed Visual Studio 2017 or later. They +include built-in support for CMake-based projects: + + https://devblogs.microsoft.com/cppblog/cmake-support-in-visual-studio/ + +For Visual Studio 2017, make sure "Visual C++ tools for CMake" is +installed; for Visual Studio 2019, make sure "C++ CMake tools for +Windows" is installed. + +### winflexbison ### + +libpcap uses the Flex lexical-analyzer generator and the Bison or +Berkeley YACC parser generators to generate the parser for filter +expressions. Windows versions of Flex and Bison can be downloaded from + + https://sourceforge.net/projects/winflexbison/ + +The downloaded file is a ZIP archive; create a folder on your C: drive, +e.g. C:\Program Files\winflexbison, and put the contents of the ZIP +archive into that folder. Then add that folder to the system PATH +environment variable. + +Git +--- + +An optional tool, required only if you will be building from a Git +repository rather than from a release source tarball, is Git. Git is +provided as an optional installation component, "Git for Windows", with +Visual Studio 2017 and later. + +Building from the Visual Studio GUI +----------------------------------- + +### Visual Studio 2017 ### + +Open the folder containing the libpcap source with Open > Folder. +Visual Studio will run CMake; however, you will need to indicate where +the Npcap or WinPcap SDK is installed. + +To do this, go to Project > "Change CMake Settings" > pcap and: + +Choose which configuration type to build, if you don't want the default +Debug build. + +In the CMakeSettings.json tab, change cmakeCommandArgs to include + + -DPacket_ROOT={path-to-sdk} + +where {path-to-sdk} is the path of the directory containing the Npcap or +WinPcap SDK. Note that backslashes in the path must be specified as two +backslashes. + +Save the configuration changes with File > "Save CMakeSettings.json" or +with control-S. + +Visual Studio will then re-run CMake. If that completes without errors, +you can build with CMake > "Build All". + +### Visual Studio 2019 ### + +Open the folder containing the libpcap source with Open > Folder. +Visual Studio will run CMake; however, you will need to indicate where +the Npcap or WinPcap SDK is installed. + +To do this, go to Project > "CMake Settings for pcap" and: + +Choose which configuration type to build, if you don't want the default +Debug build. + +Scroll down to "Cmake variables and cache", scroll through the list +looking for the entry for Packet_ROOT, and either type in the path of +the directory containing the Npcap or WinPcap SDK or use the "Browse..." +button to browse for that directory. + +Save the configuration changes with File > "Save CMakeSettings.json" or +with control-S. + +Visual Studio will then re-run CMake. If that completes without errors, +you can build with Build > "Build All". + +Building from the command line +------------------------------ + +Start the appropriate Native Tools command line prompt. + +Change to the directory into which you want to build libpcap, possibly +after creating it first. One choice is to create it as a subdirectory +of the libpcap source directory. + +Run the command + + cmake "-DPacket_ROOT={path-to-sdk}" {path-to-libpcap-source} + +where {path-to-sdk} is the path of the directory containing the Npcap or +WinPcap SDK and {path-to-libpcap-source} is the pathname of the +top-level source directory for libpcap. + +Run the command + + msbuild/m pcap.sln + +to compile libpcap. diff --git a/doc/README.aix b/doc/README.aix index 92e513ff7413..868999476af9 100644 --- a/doc/README.aix +++ b/doc/README.aix @@ -1,3 +1,25 @@ +# Compiling libpcap on AIX + +* Autoconf is expected to work everywhere. +* Neither AIX lex nor AIX yacc nor AIX m4 are suitable. + +## AIX 7.1 + +* libpcap build fails with rpcapd enabled. +* GNU M4 1.4.17 works. +* flex 2.6.4 and GNU Bison 3.5.1 work. +* CMake 3.16.0 works. +* GCC 8.3.0 works, XL C 12.1.0 works. + +## AIX 7.2 + +* libpcap build fails with rpcapd enabled. +* GNU M4 1.4.17 works. +* flex 2.5.35 and GNU Bison 3.0.4 work. +* GCC 7.2.0 works, XL C 13.1.3 works. + +## Other AIX-related information + Using BPF: (1) AIX 4.x's version of BPF is undocumented and somewhat unstandard; the @@ -27,7 +49,7 @@ Using BPF: If you fix the problems yourself, please submit a patch by forking the branch at - https://github.com/the-tcpdump-group/libpcap/issues + https://github.com/the-tcpdump-group/libpcap/tree/master and issuing a pull request, so we can incorporate the fixes into the next release. diff --git a/doc/README.dag b/doc/README.dag index 7ea25040eb1a..fd2c4b741bd7 100644 --- a/doc/README.dag +++ b/doc/README.dag @@ -1,7 +1,7 @@ The following instructions apply if you have a Linux or FreeBSD platform and want libpcap to support the DAG range of passive network monitoring cards from -Endace (http://www.endace.com, see below for further contact details). +Endace (https://www.endace.com, see below for further contact details). 1) Install and build the DAG software distribution by following the instructions supplied with that package. Current Endace customers can download @@ -22,7 +22,7 @@ If 'configure' reports that there is no DAG API, the directory may have been incorrectly specified or the DAG software was not built before configuring libpcap. -See also the libpcap INSTALL.txt file for further libpcap configuration +See also the libpcap INSTALL.md file for further libpcap configuration options. Building libpcap at this stage will include support for both the native packet @@ -117,6 +117,6 @@ Please submit bug reports via . Please also visit our Web site at: - http://www.endace.com/ + https://www.endace.com/ For more information about Endace DAG cards contact . diff --git a/doc/README.hpux b/doc/README.hpux index 65ecff97c249..4b3801b4387a 100644 --- a/doc/README.hpux +++ b/doc/README.hpux @@ -8,7 +8,7 @@ Note that packet-capture programs such as tcpdump may, on HP-UX, not be able to see packets sent from the machine on which they're running. Some articles on groups.google.com discussing this are: - http://groups.google.com/groups?selm=82ld3v%2480i%241%40mamenchi.zrz.TU-Berlin.DE + https://groups.google.com/groups?selm=82ld3v%2480i%241%40mamenchi.zrz.TU-Berlin.DE which says: @@ -69,7 +69,7 @@ which says: and - http://groups.google.com/groups?selm=38AA973E.96BE7DF7%40cc.uit.no + https://groups.google.com/groups?selm=38AA973E.96BE7DF7%40cc.uit.no which says: @@ -118,7 +118,7 @@ And another message to tcpdump-workers@tcpdump.org, from Rick Jones: Another posting: - http://groups.google.com/groups?selm=7d6gvn%24b3%241%40ocean.cup.hp.com + https://groups.google.com/groups?selm=7d6gvn%24b3%241%40ocean.cup.hp.com indicates that you need to install the optional STREAMS product to do captures on HP-UX 9.x: @@ -159,7 +159,7 @@ An additional note, from Jost Martin, for HP-UX 10.20: of an interface A: You need to get PHNE_20892,PHNE_20725 and PHCO_10947 (or newer, this is as of 4.4.00) and its dependencies. Then you can - enable the feature as descibed below: + enable the feature as described below: Patch Name: PHNE_20892 Patch Description: s700 10.20 PCI 100Base-T cumulative patch @@ -195,7 +195,7 @@ Here's the "hack_ip_stack" script: -----------------------------------Cut Here------------------------------------- #!/sbin/sh # -# nettune: hack kernel parms for safety +# nettune: hack kernel params for safety OKAY=0 ERROR=-1 diff --git a/doc/README.linux b/doc/README.linux new file mode 100644 index 000000000000..eba43aeb16c3 --- /dev/null +++ b/doc/README.linux @@ -0,0 +1,36 @@ +Currently, libpcap supports packet capturing on Linux 2.6.27 and later; +earlier versions are not supported. + +You must configure 2.6.x kernels with the CONFIG_PACKET_MMAP option for +this protocol. 3.x and later kernels do not require that. + +Note that, by default, libpcap will, if libnl is present, build with it; +it uses libnl to support monitor mode on mac80211 devices. There is a +configuration option to disable building with libnl, but, if that option +is chosen, the monitor-mode APIs (as used by tcpdump's "-I" flag, and as +will probably be used by other applications in the future) won't work +properly on mac80211 devices. + +Linux's run-time linker allows shared libraries to be linked with other +shared libraries, which means that if an older version of a shared +library doesn't require routines from some other shared library, and a +later version of the shared library does require those routines, the +later version of the shared library can be linked with that other shared +library and, if it's otherwise binary-compatible with the older version, +can replace that older version without breaking applications built with +the older version, and without breaking configure scripts or the build +procedure for applications whose configure script doesn't use the +pcap-config script if they build with the shared library. (The build +procedure for applications whose configure scripts use the pcap-config +script if present will not break even if they build with the static +library.) + +Statistics: +Statistics reported by pcap are platform specific. The statistics +reported by pcap_stats on Linux are as follows: + +ps_recv Number of packets that were accepted by the pcap filter +ps_drop Number of packets that had passed filtering but were not + passed on to pcap due to things like buffer shortage, etc. + This is useful because these are packets you are interested in + but won't be reported by, for example, tcpdump output. diff --git a/doc/README.linux.md b/doc/README.linux.md deleted file mode 100644 index ddca4fecfa9e..000000000000 --- a/doc/README.linux.md +++ /dev/null @@ -1,108 +0,0 @@ -In order for libpcap to be able to capture packets on a Linux system, -the "packet" protocol must be supported by your kernel. If it is not, -you may get error messages such as - - modprobe: can't locate module net-pf-17 - -in "/var/adm/messages", or may get messages such as - - socket: Address family not supported by protocol - -from applications using libpcap. - -You must configure the kernel with the CONFIG_PACKET option for this -protocol; the following note is from the Linux "Configure.help" file for -the 2.0[.x] kernel: - - Packet socket - CONFIG_PACKET - The Packet protocol is used by applications which communicate - directly with network devices without an intermediate network - protocol implemented in the kernel, e.g. tcpdump. If you want them - to work, choose Y. - - This driver is also available as a module called af_packet.o ( = - code which can be inserted in and removed from the running kernel - whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt; if you use modprobe or - kmod, you may also want to add "alias net-pf-17 af_packet" to - /etc/modules.conf. - -and the note for the 2.2[.x] kernel says: - - Packet socket - CONFIG_PACKET - The Packet protocol is used by applications which communicate - directly with network devices without an intermediate network - protocol implemented in the kernel, e.g. tcpdump. If you want them - to work, choose Y. This driver is also available as a module called - af_packet.o ( = code which can be inserted in and removed from the - running kernel whenever you want). If you want to compile it as a - module, say M here and read Documentation/modules.txt. You will - need to add 'alias net-pf-17 af_packet' to your /etc/conf.modules - file for the module version to function automatically. If unsure, - say Y. - -In addition, there is an option that, in 2.2 and later kernels, will -allow packet capture filters specified to programs such as tcpdump to be -executed in the kernel, so that packets that don't pass the filter won't -be copied from the kernel to the program, rather than having all packets -copied to the program and libpcap doing the filtering in user mode. - -Copying packets from the kernel to the program consumes a significant -amount of CPU, so filtering in the kernel can reduce the overhead of -capturing packets if a filter has been specified that discards a -significant number of packets. (If no filter is specified, it makes no -difference whether the filtering isn't performed in the kernel or isn't -performed in user mode. :-)) - -The option for this is the CONFIG_FILTER option; the "Configure.help" -file says: - - Socket filtering - CONFIG_FILTER - The Linux Socket Filter is derived from the Berkeley Packet Filter. - If you say Y here, user-space programs can attach a filter to any - socket and thereby tell the kernel that it should allow or disallow - certain types of data to get through the socket. Linux Socket - Filtering works on all socket types except TCP for now. See the text - file linux/Documentation/networking/filter.txt for more information. - If unsure, say N. - -Note that, by default, libpcap will, if libnl is present, build with it; -it uses libnl to support monitor mode on mac80211 devices. There is a -configuration option to disable building with libnl, but, if that option -is chosen, the monitor-mode APIs (as used by tcpdump's "-I" flag, and as -will probably be used by other applications in the future) won't work -properly on mac80211 devices. - -Linux's run-time linker allows shared libraries to be linked with other -shared libraries, which means that if an older version of a shared -library doesn't require routines from some other shared library, and a -later version of the shared library does require those routines, the -later version of the shared library can be linked with that other shared -library and, if it's otherwise binary-compatible with the older version, -can replace that older version without breaking applications built with -the older version, and without breaking configure scripts or the build -procedure for applications whose configure script doesn't use the -pcap-config script if they build with the shared library. (The build -procedure for applications whose configure scripts use the pcap-config -script if present will not break even if they build with the static -library.) - -Statistics: -Statistics reported by pcap are platform specific. The statistics -reported by pcap_stats on Linux are as follows: - -2.2.x -===== -ps_recv Number of packets that were accepted by the pcap filter -ps_drop Always 0, this statistic is not gathered on this platform - -2.4.x and later -===== -ps_recv Number of packets that were accepted by the pcap filter -ps_drop Number of packets that had passed filtering but were not - passed on to pcap due to things like buffer shortage, etc. - This is useful because these are packets you are interested in - but won't be reported by, for example, tcpdump output. diff --git a/doc/README.septel b/doc/README.septel index fa2c0c9a40d9..d7fb5c7c61a7 100644 --- a/doc/README.septel +++ b/doc/README.septel @@ -1,6 +1,6 @@ The following instructions apply if you have a Linux platform and want libpcap to support the Septel range of passive network monitoring cards -from Intel (http://www.intel.com) +from Intel (https://www.intel.com) 1) Install and build the Septel software distribution by following the instructions supplied with that package. @@ -25,7 +25,7 @@ If 'configure' reports that there is no Septel API, the directory may have been incorrectly specified or the Septel software was not built before configuring libpcap. -See also the libpcap INSTALL.txt file for further libpcap configuration +See also the libpcap INSTALL.md file for further libpcap configuration options. Building libpcap at this stage will include support for both the native diff --git a/doc/README.sita b/doc/README.sita index 5a65822ecedb..c85e0d8ceab4 100644 --- a/doc/README.sita +++ b/doc/README.sita @@ -1,18 +1,25 @@ +NOTE: this is not currently supported; the configure script doesn't +support --with-sita, and CMake doesn't support enabling SITA ACN +support. The code currently does not compile; it should really be +implemented as an additional remote capture mechanism, using a URL, +rather than as a separate version of libpcap that supports only the ACN +product, but the infrastructure for that isn't yet available. + The following instructions apply if you have a Linux platform and want libpcap to support the 'ACN' WAN/LAN router product from SITA -(http://www.sita.aero) +(https://www.sita.aero) This might also work on non-Linux Unix-compatible platforms, but that has not been tested. -See also the libpcap INSTALL.txt file for further libpcap configuration +See also the libpcap INSTALL.md file for further libpcap configuration options. These additions/extensions have been made to PCAP to allow it to capture packets from a SITA ACN device (and potentially others). To enable its support you need to ensure that the distribution has -a correct configure.ac file; that can be created if neccessay by +a correct configure.ac file; that can be created if necessary by using the normal autoconf procedure of: aclocal diff --git a/doc/README.solaris.md b/doc/README.solaris.md new file mode 100644 index 000000000000..06ba789dfc4c --- /dev/null +++ b/doc/README.solaris.md @@ -0,0 +1,58 @@ +# Compiling libpcap on Solaris and related OSes + +* Autoconf works everywhere. +* Neither Solaris lex nor Solaris yacc are suitable. +* Neither illumos lex nor illumos yacc are suitable. +* Solaris m4 and illumos m4 are suitable. + +## OmniOS r151042/AMD64 + +* flex 2.6.4 and GNU Bison 3.8.2 work. +* CMake 3.23.1 works. +* GCC 11.2.0 and Clang 14.0.3 work. + +## OpenIndiana 2021.04/AMD64 + +* flex 2.6.4 and GNU Bison 3.7.6 work. +* CMake 3.21.1 works. +* GCC 7.5.0 and GCC 10.3.0 work, Clang 9.0.1 works. + +For reference, the tests were done using a system installed from +`OI-hipster-text-20210430.iso` plus the following packages: +```shell +xargs -L1 pkg install <): - - option PACKETFILTER - -or use "doconfig" without any arguments to add the packet filter driver -option via the kernel option menu (see the system administration -documentation for information on how to do this). - -Device configuration --------------------- - -Devices used for packet filtering must be created thanks to -the following command (executed in the /dev directory): - - ./MAKEDEV pfilt - -Interface configuration ------------------------ - -In order to capture all packets on a network, you may want to allow -applications to put the interface on that network into "local copy" -mode, so that tcpdump can see packets sent by the host on which it's -running as well as packets received by that host, and to put the -interface into "promiscuous" mode, so that tcpdump can see packets on -the network segment not sent to the host on which it's running, by using -the pfconfig(1) command: - - pfconfig +c +p - -or allow application to put any interface into "local copy" or -"promiscuous" mode by using the command: - - pfconfig +c +p -a - -Note: all instructions given require root privileges. diff --git a/etherent.c b/etherent.c index 5f499613e088..69da9a540ad8 100644 --- a/etherent.c +++ b/etherent.c @@ -25,7 +25,6 @@ #include -#include #include #include #include @@ -45,14 +44,18 @@ static inline int skip_line(FILE *); static inline u_char xdtoi(u_char c) { - if (isdigit(c)) + if (c >= '0' && c <= '9') return (u_char)(c - '0'); - else if (islower(c)) + else if (c >= 'a' && c <= 'f') return (u_char)(c - 'a' + 10); else return (u_char)(c - 'A' + 10); } +/* + * Skip linear white space (space and tab) and any CRs before LF. + * Stop when we hit a non-white-space character or an end-of-line LF. + */ static inline int skip_space(FILE *f) { @@ -60,7 +63,7 @@ skip_space(FILE *f) do { c = getc(f); - } while (isspace(c) && c != '\n'); + } while (c == ' ' || c == '\t' || c == '\r'); return c; } @@ -97,7 +100,7 @@ pcap_next_etherent(FILE *fp) /* If this is a comment, or first thing on line cannot be Ethernet address, skip the line. */ - if (!isxdigit(c)) { + if (!PCAP_ISXDIGIT(c)) { c = skip_line(fp); if (c == EOF) return (NULL); @@ -110,7 +113,7 @@ pcap_next_etherent(FILE *fp) c = getc(fp); if (c == EOF) return (NULL); - if (isxdigit(c)) { + if (PCAP_ISXDIGIT(c)) { d <<= 4; d |= xdtoi((u_char)c); c = getc(fp); @@ -126,7 +129,7 @@ pcap_next_etherent(FILE *fp) } /* Must be whitespace */ - if (!isspace(c)) { + if (c != ' ' && c != '\t' && c != '\r' && c != '\n') { c = skip_line(fp); if (c == EOF) return (NULL); @@ -156,7 +159,8 @@ pcap_next_etherent(FILE *fp) c = getc(fp); if (c == EOF) return (NULL); - } while (!isspace(c) && --namesize != 0); + } while (c != ' ' && c != '\t' && c != '\r' && c != '\n' + && --namesize != 0); *bp = '\0'; /* Eat trailing junk */ diff --git a/ethertype.h b/ethertype.h index 51f63083f8b8..e34e07b98da3 100644 --- a/ethertype.h +++ b/ethertype.h @@ -32,58 +32,58 @@ */ #ifndef ETHERTYPE_PUP -#define ETHERTYPE_PUP 0x0200 /* PUP protocol */ +#define ETHERTYPE_PUP 0x0200 /* PUP protocol */ #endif #ifndef ETHERTYPE_IP -#define ETHERTYPE_IP 0x0800 /* IP protocol */ +#define ETHERTYPE_IP 0x0800 /* IP protocol */ #endif #ifndef ETHERTYPE_ARP #define ETHERTYPE_ARP 0x0806 /* Addr. resolution protocol */ #endif -#ifndef ETHERTYPE_REVARP -#define ETHERTYPE_REVARP 0x8035 /* reverse Addr. resolution protocol */ -#endif #ifndef ETHERTYPE_NS #define ETHERTYPE_NS 0x0600 #endif #ifndef ETHERTYPE_SPRITE -#define ETHERTYPE_SPRITE 0x0500 +#define ETHERTYPE_SPRITE 0x0500 #endif #ifndef ETHERTYPE_TRAIL #define ETHERTYPE_TRAIL 0x1000 #endif #ifndef ETHERTYPE_MOPDL -#define ETHERTYPE_MOPDL 0x6001 +#define ETHERTYPE_MOPDL 0x6001 #endif #ifndef ETHERTYPE_MOPRC -#define ETHERTYPE_MOPRC 0x6002 +#define ETHERTYPE_MOPRC 0x6002 #endif #ifndef ETHERTYPE_DN -#define ETHERTYPE_DN 0x6003 +#define ETHERTYPE_DN 0x6003 #endif #ifndef ETHERTYPE_LAT -#define ETHERTYPE_LAT 0x6004 +#define ETHERTYPE_LAT 0x6004 #endif #ifndef ETHERTYPE_SCA #define ETHERTYPE_SCA 0x6007 #endif +#ifndef ETHERTYPE_TEB +#define ETHERTYPE_TEB 0x6558 +#endif #ifndef ETHERTYPE_REVARP -#define ETHERTYPE_REVARP 0x8035 +#define ETHERTYPE_REVARP 0x8035 /* reverse Addr. resolution protocol */ #endif #ifndef ETHERTYPE_LANBRIDGE -#define ETHERTYPE_LANBRIDGE 0x8038 +#define ETHERTYPE_LANBRIDGE 0x8038 #endif #ifndef ETHERTYPE_DECDNS -#define ETHERTYPE_DECDNS 0x803c +#define ETHERTYPE_DECDNS 0x803c #endif #ifndef ETHERTYPE_DECDTS -#define ETHERTYPE_DECDTS 0x803e +#define ETHERTYPE_DECDTS 0x803e #endif #ifndef ETHERTYPE_VEXP -#define ETHERTYPE_VEXP 0x805b +#define ETHERTYPE_VEXP 0x805b #endif #ifndef ETHERTYPE_VPROD -#define ETHERTYPE_VPROD 0x805c +#define ETHERTYPE_VPROD 0x805c #endif #ifndef ETHERTYPE_ATALK #define ETHERTYPE_ATALK 0x809b @@ -101,10 +101,10 @@ #define ETHERTYPE_IPV6 0x86dd #endif #ifndef ETHERTYPE_MPLS -#define ETHERTYPE_MPLS 0x8847 +#define ETHERTYPE_MPLS 0x8847 #endif #ifndef ETHERTYPE_MPLS_MULTI -#define ETHERTYPE_MPLS_MULTI 0x8848 +#define ETHERTYPE_MPLS_MULTI 0x8848 #endif #ifndef ETHERTYPE_PPPOED #define ETHERTYPE_PPPOED 0x8863 @@ -116,7 +116,7 @@ #define ETHERTYPE_8021AD 0x88a8 #endif #ifndef ETHERTYPE_LOOPBACK -#define ETHERTYPE_LOOPBACK 0x9000 +#define ETHERTYPE_LOOPBACK 0x9000 #endif #ifndef ETHERTYPE_8021QINQ #define ETHERTYPE_8021QINQ 0x9100 diff --git a/extract.h b/extract.h index aa3ff99196e3..33579b1155b5 100644 --- a/extract.h +++ b/extract.h @@ -25,15 +25,94 @@ #include #include +#include "portability.h" /* - * Macros to extract possibly-unaligned big-endian integral values. + * If we have versions of GCC or Clang that support an __attribute__ + * to say "if we're building with unsigned behavior sanitization, + * don't complain about undefined behavior in this function", we + * label these functions with that attribute - we *know* it's undefined + * in the C standard, but we *also* know it does what we want with + * the ISA we're targeting and the compiler we're using. + * + * For GCC 4.9.0 and later, we use __attribute__((no_sanitize_undefined)); + * pre-5.0 GCC doesn't have __has_attribute, and I'm not sure whether + * GCC or Clang first had __attribute__((no_sanitize(XXX)). + * + * For Clang, we check for __attribute__((no_sanitize(XXX)) with + * __has_attribute, as there are versions of Clang that support + * __attribute__((no_sanitize("undefined")) but don't support + * __attribute__((no_sanitize_undefined)). + * + * We define this here, rather than in funcattrs.h, because we + * only want it used here, we don't want it to be broadly used. + * (Any printer will get this defined, but this should at least + * make it harder for people to find.) */ -#ifdef LBL_ALIGN +#if defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 409) +#define UNALIGNED_OK __attribute__((no_sanitize_undefined)) +#elif __has_attribute(no_sanitize) +#define UNALIGNED_OK __attribute__((no_sanitize("undefined"))) +#else +#define UNALIGNED_OK +#endif + +#if (defined(__i386__) || defined(_M_IX86) || defined(__X86__) || defined(__x86_64__) || defined(_M_X64)) || \ + (defined(__m68k__) && (!defined(__mc68000__) && !defined(__mc68010__))) || \ + (defined(__ppc__) || defined(__ppc64__) || defined(_M_PPC) || defined(_ARCH_PPC) || defined(_ARCH_PPC64)) || \ + (defined(__s390__) || defined(__s390x__) || defined(__zarch__)) /* - * The processor doesn't natively handle unaligned loads. + * The processor natively handles unaligned loads, so we can just + * cast the pointer and fetch through it. + * + * XXX - are those all the x86 tests we need? + * XXX - are those the only 68k tests we need not to generated + * unaligned accesses if the target is the 68000 or 68010? + * XXX - are there any tests we don't need, because some definitions are for + * compilers that also predefine the GCC symbols? + * XXX - do we need to test for both 32-bit and 64-bit versions of those + * architectures in all cases? */ -#if PCAP_IS_AT_LEAST_GNUC_VERSION(2,0) && \ +UNALIGNED_OK static inline uint16_t +EXTRACT_BE_U_2(const void *p) +{ + return ((uint16_t)ntohs(*(const uint16_t *)(p))); +} + +UNALIGNED_OK static inline int16_t +EXTRACT_BE_S_2(const void *p) +{ + return ((int16_t)ntohs(*(const int16_t *)(p))); +} + +UNALIGNED_OK static inline uint32_t +EXTRACT_BE_U_4(const void *p) +{ + return ((uint32_t)ntohl(*(const uint32_t *)(p))); +} + +UNALIGNED_OK static inline int32_t +EXTRACT_BE_S_4(const void *p) +{ + return ((int32_t)ntohl(*(const int32_t *)(p))); +} + +UNALIGNED_OK static inline uint64_t +EXTRACT_BE_U_8(const void *p) +{ + return ((uint64_t)(((uint64_t)ntohl(*((const uint32_t *)(p) + 0))) << 32 | + ((uint64_t)ntohl(*((const uint32_t *)(p) + 1))) << 0)); + +} + +UNALIGNED_OK static inline int64_t +EXTRACT_BE_S_8(const void *p) +{ + return ((int64_t)(((int64_t)ntohl(*((const uint32_t *)(p) + 0))) << 32 | + ((uint64_t)ntohl(*((const uint32_t *)(p) + 1))) << 0)); + +} +#elif PCAP_IS_AT_LEAST_GNUC_VERSION(2,0) && \ (defined(__alpha) || defined(__alpha__) || \ defined(__mips) || defined(__mips__)) /* @@ -48,11 +127,11 @@ * cast the pointer to point to one of those, and fetch through it; * the GCC manual doesn't appear to explicitly say that * __attribute__((packed)) causes the compiler to generate unaligned-safe - * code, but it apppears to do so. + * code, but it appears to do so. * * We do this in case the compiler can generate code using those * instructions to do an unaligned load and pass stuff to "ntohs()" or - * "ntohl()", which might be better than than the code to fetch the + * "ntohl()", which might be better than the code to fetch the * bytes one at a time and assemble them. (That might not be the * case on a little-endian platform, such as DEC's MIPS machines and * Alpha machines, where "ntohs()" and "ntohl()" might not be done @@ -92,46 +171,91 @@ typedef struct { uint16_t val; } __attribute__((packed)) unaligned_uint16_t; +typedef struct { + int16_t val; +} __attribute__((packed)) unaligned_int16_t; + typedef struct { uint32_t val; } __attribute__((packed)) unaligned_uint32_t; -static inline uint16_t -EXTRACT_16BITS(const void *p) +typedef struct { + int32_t val; +} __attribute__((packed)) unaligned_int32_t; + +UNALIGNED_OK static inline uint16_t +EXTRACT_BE_U_2(const void *p) { return ((uint16_t)ntohs(((const unaligned_uint16_t *)(p))->val)); } -static inline uint32_t -EXTRACT_32BITS(const void *p) +UNALIGNED_OK static inline int16_t +EXTRACT_BE_S_2(const void *p) +{ + return ((int16_t)ntohs(((const unaligned_int16_t *)(p))->val)); +} + +UNALIGNED_OK static inline uint32_t +EXTRACT_BE_U_4(const void *p) { return ((uint32_t)ntohl(((const unaligned_uint32_t *)(p))->val)); } -static inline uint64_t -EXTRACT_64BITS(const void *p) +UNALIGNED_OK static inline int32_t +EXTRACT_BE_S_4(const void *p) { - return ((uint64_t)(((uint64_t)ntohl(((const unaligned_uint32_t *)(p) + 0)->val)) << 32 | \ + return ((int32_t)ntohl(((const unaligned_int32_t *)(p))->val)); +} + +UNALIGNED_OK static inline uint64_t +EXTRACT_BE_U_8(const void *p) +{ + return ((uint64_t)(((uint64_t)ntohl(((const unaligned_uint32_t *)(p) + 0)->val)) << 32 | ((uint64_t)ntohl(((const unaligned_uint32_t *)(p) + 1)->val)) << 0)); } -#else /* have to do it a byte at a time */ +UNALIGNED_OK static inline int64_t +EXTRACT_BE_S_8(const void *p) +{ + return ((int64_t)(((uint64_t)ntohl(((const unaligned_uint32_t *)(p) + 0)->val)) << 32 | + ((uint64_t)ntohl(((const unaligned_uint32_t *)(p) + 1)->val)) << 0)); +} +#else /* - * This isn't a GCC-compatible compiler, we don't have __attribute__, + * This architecture doesn't natively support unaligned loads, and either + * this isn't a GCC-compatible compiler, we don't have __attribute__, * or we do but we don't know of any better way with this instruction * set to do unaligned loads, so do unaligned loads of big-endian * quantities the hard way - fetch the bytes one at a time and * assemble them. + * + * XXX - ARM is a special case. ARMv1 through ARMv5 didn't suppory + * unaligned loads; ARMv6 and later support it *but* have a bit in + * the system control register that the OS can set and that causes + * unaligned loads to fault rather than succeeding. + * + * At least some OSes may set that flag, so we do *not* treat ARM + * as supporting unaligned loads. If your OS supports them on ARM, + * and you want to use them, please update the tests in the #if above + * to check for ARM *and* for your OS. */ -#define EXTRACT_16BITS(p) \ +#define EXTRACT_BE_U_2(p) \ ((uint16_t)(((uint16_t)(*((const uint8_t *)(p) + 0)) << 8) | \ ((uint16_t)(*((const uint8_t *)(p) + 1)) << 0))) -#define EXTRACT_32BITS(p) \ +#define EXTRACT_BE_S_2(p) \ + ((int16_t)(((uint16_t)(*((const uint8_t *)(p) + 0)) << 8) | \ + ((uint16_t)(*((const uint8_t *)(p) + 1)) << 0))) +#define EXTRACT_BE_U_4(p) \ ((uint32_t)(((uint32_t)(*((const uint8_t *)(p) + 0)) << 24) | \ ((uint32_t)(*((const uint8_t *)(p) + 1)) << 16) | \ ((uint32_t)(*((const uint8_t *)(p) + 2)) << 8) | \ ((uint32_t)(*((const uint8_t *)(p) + 3)) << 0))) -#define EXTRACT_64BITS(p) \ +#define EXTRACT_BE_S_4(p) \ + ((int32_t)(((uint32_t)(*((const uint8_t *)(p) + 0)) << 24) | \ + ((uint32_t)(*((const uint8_t *)(p) + 1)) << 16) | \ + ((uint32_t)(*((const uint8_t *)(p) + 2)) << 8) | \ + ((uint32_t)(*((const uint8_t *)(p) + 3)) << 0))) +#define EXTRACT_BE_U_8(p) \ ((uint64_t)(((uint64_t)(*((const uint8_t *)(p) + 0)) << 56) | \ ((uint64_t)(*((const uint8_t *)(p) + 1)) << 48) | \ ((uint64_t)(*((const uint8_t *)(p) + 2)) << 40) | \ @@ -140,47 +264,67 @@ EXTRACT_64BITS(const void *p) ((uint64_t)(*((const uint8_t *)(p) + 5)) << 16) | \ ((uint64_t)(*((const uint8_t *)(p) + 6)) << 8) | \ ((uint64_t)(*((const uint8_t *)(p) + 7)) << 0))) -#endif /* must special-case unaligned accesses */ -#else /* LBL_ALIGN */ +#define EXTRACT_BE_S_8(p) \ + ((int64_t)(((uint64_t)(*((const uint8_t *)(p) + 0)) << 56) | \ + ((uint64_t)(*((const uint8_t *)(p) + 1)) << 48) | \ + ((uint64_t)(*((const uint8_t *)(p) + 2)) << 40) | \ + ((uint64_t)(*((const uint8_t *)(p) + 3)) << 32) | \ + ((uint64_t)(*((const uint8_t *)(p) + 4)) << 24) | \ + ((uint64_t)(*((const uint8_t *)(p) + 5)) << 16) | \ + ((uint64_t)(*((const uint8_t *)(p) + 6)) << 8) | \ + ((uint64_t)(*((const uint8_t *)(p) + 7)) << 0))) + /* - * The processor natively handles unaligned loads, so we can just - * cast the pointer and fetch through it. + * Extract an IPv4 address, which is in network byte order, and not + * necessarily aligned, and provide the result in host byte order. */ -static inline uint16_t -EXTRACT_16BITS(const void *p) -{ - return ((uint16_t)ntohs(*(const uint16_t *)(p))); -} +#define EXTRACT_IPV4_TO_HOST_ORDER(p) \ + ((uint32_t)(((uint32_t)(*((const uint8_t *)(p) + 0)) << 24) | \ + ((uint32_t)(*((const uint8_t *)(p) + 1)) << 16) | \ + ((uint32_t)(*((const uint8_t *)(p) + 2)) << 8) | \ + ((uint32_t)(*((const uint8_t *)(p) + 3)) << 0))) +#endif /* unaligned access checks */ -static inline uint32_t -EXTRACT_32BITS(const void *p) -{ - return ((uint32_t)ntohl(*(const uint32_t *)(p))); -} - -static inline uint64_t -EXTRACT_64BITS(const void *p) -{ - return ((uint64_t)(((uint64_t)ntohl(*((const uint32_t *)(p) + 0))) << 32 | \ - ((uint64_t)ntohl(*((const uint32_t *)(p) + 1))) << 0)); - -} - -#endif /* LBL_ALIGN */ - -#define EXTRACT_24BITS(p) \ +/* + * Non-power-of-2 sizes. + */ +#define EXTRACT_BE_U_3(p) \ ((uint32_t)(((uint32_t)(*((const uint8_t *)(p) + 0)) << 16) | \ ((uint32_t)(*((const uint8_t *)(p) + 1)) << 8) | \ ((uint32_t)(*((const uint8_t *)(p) + 2)) << 0))) -#define EXTRACT_40BITS(p) \ +#define EXTRACT_BE_S_3(p) \ + (((*((const uint8_t *)(p) + 0)) & 0x80) ? \ + ((int32_t)(((uint32_t)(*((const uint8_t *)(p) + 0)) << 16) | \ + ((uint32_t)(*((const uint8_t *)(p) + 1)) << 8) | \ + ((uint32_t)(*((const uint8_t *)(p) + 2)) << 0))) : \ + ((int32_t)(0xFF000000U | \ + ((uint32_t)(*((const uint8_t *)(p) + 0)) << 16) | \ + ((uint32_t)(*((const uint8_t *)(p) + 1)) << 8) | \ + ((uint32_t)(*((const uint8_t *)(p) + 2)) << 0)))) + +#define EXTRACT_BE_U_5(p) \ ((uint64_t)(((uint64_t)(*((const uint8_t *)(p) + 0)) << 32) | \ ((uint64_t)(*((const uint8_t *)(p) + 1)) << 24) | \ ((uint64_t)(*((const uint8_t *)(p) + 2)) << 16) | \ ((uint64_t)(*((const uint8_t *)(p) + 3)) << 8) | \ ((uint64_t)(*((const uint8_t *)(p) + 4)) << 0))) -#define EXTRACT_48BITS(p) \ +#define EXTRACT_BE_S_5(p) \ + (((*((const uint8_t *)(p) + 0)) & 0x80) ? \ + ((int64_t)(((uint64_t)(*((const uint8_t *)(p) + 0)) << 32) | \ + ((uint64_t)(*((const uint8_t *)(p) + 1)) << 24) | \ + ((uint64_t)(*((const uint8_t *)(p) + 2)) << 16) | \ + ((uint64_t)(*((const uint8_t *)(p) + 3)) << 8) | \ + ((uint64_t)(*((const uint8_t *)(p) + 4)) << 0))) : \ + ((int64_t)(INT64_T_CONSTANT(0xFFFFFF0000000000U) | \ + ((uint64_t)(*((const uint8_t *)(p) + 0)) << 32) | \ + ((uint64_t)(*((const uint8_t *)(p) + 1)) << 24) | \ + ((uint64_t)(*((const uint8_t *)(p) + 2)) << 16) | \ + ((uint64_t)(*((const uint8_t *)(p) + 3)) << 8) | \ + ((uint64_t)(*((const uint8_t *)(p) + 4)) << 0)))) + +#define EXTRACT_BE_U_6(p) \ ((uint64_t)(((uint64_t)(*((const uint8_t *)(p) + 0)) << 40) | \ ((uint64_t)(*((const uint8_t *)(p) + 1)) << 32) | \ ((uint64_t)(*((const uint8_t *)(p) + 2)) << 24) | \ @@ -188,7 +332,23 @@ EXTRACT_64BITS(const void *p) ((uint64_t)(*((const uint8_t *)(p) + 4)) << 8) | \ ((uint64_t)(*((const uint8_t *)(p) + 5)) << 0))) -#define EXTRACT_56BITS(p) \ +#define EXTRACT_BE_S_6(p) \ + (((*((const uint8_t *)(p) + 0)) & 0x80) ? \ + ((int64_t)(((uint64_t)(*((const uint8_t *)(p) + 0)) << 40) | \ + ((uint64_t)(*((const uint8_t *)(p) + 1)) << 32) | \ + ((uint64_t)(*((const uint8_t *)(p) + 2)) << 24) | \ + ((uint64_t)(*((const uint8_t *)(p) + 3)) << 16) | \ + ((uint64_t)(*((const uint8_t *)(p) + 4)) << 8) | \ + ((uint64_t)(*((const uint8_t *)(p) + 5)) << 0))) : \ + ((int64_t)(INT64_T_CONSTANT(0xFFFFFFFF00000000U) | \ + ((uint64_t)(*((const uint8_t *)(p) + 0)) << 40) | \ + ((uint64_t)(*((const uint8_t *)(p) + 1)) << 32) | \ + ((uint64_t)(*((const uint8_t *)(p) + 2)) << 24) | \ + ((uint64_t)(*((const uint8_t *)(p) + 3)) << 16) | \ + ((uint64_t)(*((const uint8_t *)(p) + 4)) << 8) | \ + ((uint64_t)(*((const uint8_t *)(p) + 5)) << 0)))) + +#define EXTRACT_BE_U_7(p) \ ((uint64_t)(((uint64_t)(*((const uint8_t *)(p) + 0)) << 48) | \ ((uint64_t)(*((const uint8_t *)(p) + 1)) << 40) | \ ((uint64_t)(*((const uint8_t *)(p) + 2)) << 32) | \ @@ -197,24 +357,53 @@ EXTRACT_64BITS(const void *p) ((uint64_t)(*((const uint8_t *)(p) + 5)) << 8) | \ ((uint64_t)(*((const uint8_t *)(p) + 6)) << 0))) +#define EXTRACT_BE_S_7(p) \ + (((*((const uint8_t *)(p) + 0)) & 0x80) ? \ + ((int64_t)(((uint64_t)(*((const uint8_t *)(p) + 0)) << 48) | \ + ((uint64_t)(*((const uint8_t *)(p) + 1)) << 40) | \ + ((uint64_t)(*((const uint8_t *)(p) + 2)) << 32) | \ + ((uint64_t)(*((const uint8_t *)(p) + 3)) << 24) | \ + ((uint64_t)(*((const uint8_t *)(p) + 4)) << 16) | \ + ((uint64_t)(*((const uint8_t *)(p) + 5)) << 8) | \ + ((uint64_t)(*((const uint8_t *)(p) + 6)) << 0))) : \ + ((int64_t)(INT64_T_CONSTANT(0xFFFFFFFFFF000000U) | \ + ((uint64_t)(*((const uint8_t *)(p) + 0)) << 48) | \ + ((uint64_t)(*((const uint8_t *)(p) + 1)) << 40) | \ + ((uint64_t)(*((const uint8_t *)(p) + 2)) << 32) | \ + ((uint64_t)(*((const uint8_t *)(p) + 3)) << 24) | \ + ((uint64_t)(*((const uint8_t *)(p) + 4)) << 16) | \ + ((uint64_t)(*((const uint8_t *)(p) + 5)) << 8) | \ + ((uint64_t)(*((const uint8_t *)(p) + 6)) << 0)))) + /* * Macros to extract possibly-unaligned little-endian integral values. * XXX - do loads on little-endian machines that support unaligned loads? */ -#define EXTRACT_LE_8BITS(p) (*(p)) -#define EXTRACT_LE_16BITS(p) \ +#define EXTRACT_LE_U_2(p) \ ((uint16_t)(((uint16_t)(*((const uint8_t *)(p) + 1)) << 8) | \ ((uint16_t)(*((const uint8_t *)(p) + 0)) << 0))) -#define EXTRACT_LE_32BITS(p) \ +#define EXTRACT_LE_S_2(p) \ + ((int16_t)(((uint16_t)(*((const uint8_t *)(p) + 1)) << 8) | \ + ((uint16_t)(*((const uint8_t *)(p) + 0)) << 0))) +#define EXTRACT_LE_U_4(p) \ ((uint32_t)(((uint32_t)(*((const uint8_t *)(p) + 3)) << 24) | \ ((uint32_t)(*((const uint8_t *)(p) + 2)) << 16) | \ ((uint32_t)(*((const uint8_t *)(p) + 1)) << 8) | \ ((uint32_t)(*((const uint8_t *)(p) + 0)) << 0))) -#define EXTRACT_LE_24BITS(p) \ +#define EXTRACT_LE_S_4(p) \ + ((int32_t)(((uint32_t)(*((const uint8_t *)(p) + 3)) << 24) | \ + ((uint32_t)(*((const uint8_t *)(p) + 2)) << 16) | \ + ((uint32_t)(*((const uint8_t *)(p) + 1)) << 8) | \ + ((uint32_t)(*((const uint8_t *)(p) + 0)) << 0))) +#define EXTRACT_LE_U_3(p) \ ((uint32_t)(((uint32_t)(*((const uint8_t *)(p) + 2)) << 16) | \ ((uint32_t)(*((const uint8_t *)(p) + 1)) << 8) | \ ((uint32_t)(*((const uint8_t *)(p) + 0)) << 0))) -#define EXTRACT_LE_64BITS(p) \ +#define EXTRACT_LE_S_3(p) \ + ((int32_t)(((uint32_t)(*((const uint8_t *)(p) + 2)) << 16) | \ + ((uint32_t)(*((const uint8_t *)(p) + 1)) << 8) | \ + ((uint32_t)(*((const uint8_t *)(p) + 0)) << 0))) +#define EXTRACT_LE_U_8(p) \ ((uint64_t)(((uint64_t)(*((const uint8_t *)(p) + 7)) << 56) | \ ((uint64_t)(*((const uint8_t *)(p) + 6)) << 48) | \ ((uint64_t)(*((const uint8_t *)(p) + 5)) << 40) | \ @@ -223,3 +412,12 @@ EXTRACT_64BITS(const void *p) ((uint64_t)(*((const uint8_t *)(p) + 2)) << 16) | \ ((uint64_t)(*((const uint8_t *)(p) + 1)) << 8) | \ ((uint64_t)(*((const uint8_t *)(p) + 0)) << 0))) +#define EXTRACT_LE_S_8(p) \ + ((int64_t)(((uint64_t)(*((const uint8_t *)(p) + 7)) << 56) | \ + ((uint64_t)(*((const uint8_t *)(p) + 6)) << 48) | \ + ((uint64_t)(*((const uint8_t *)(p) + 5)) << 40) | \ + ((uint64_t)(*((const uint8_t *)(p) + 4)) << 32) | \ + ((uint64_t)(*((const uint8_t *)(p) + 3)) << 24) | \ + ((uint64_t)(*((const uint8_t *)(p) + 2)) << 16) | \ + ((uint64_t)(*((const uint8_t *)(p) + 1)) << 8) | \ + ((uint64_t)(*((const uint8_t *)(p) + 0)) << 0))) diff --git a/fad-getad.c b/fad-getad.c index 5236fbb94cda..ba8f97538a58 100644 --- a/fad-getad.c +++ b/fad-getad.c @@ -42,7 +42,6 @@ #include -#include #include #include #include @@ -190,7 +189,7 @@ pcap_findalldevs_interfaces(pcap_if_list_t *devlistp, char *errbuf, * We have a ":"; is it followed by a number? */ q = p + 1; - while (isdigit((unsigned char)*q)) + while (PCAP_ISDIGIT(*q)) q++; if (*q == '\0') { /* diff --git a/fad-gifc.c b/fad-gifc.c index 6b1612747582..8940876a579e 100644 --- a/fad-gifc.c +++ b/fad-gifc.c @@ -49,19 +49,13 @@ struct rtentry; /* declarations in */ #include #include -#include #include #include #include #include #include #include - -#ifdef HAVE_LIMITS_H #include -#else -#define INT_MAX 2147483647 -#endif #include "pcap-int.h" @@ -178,7 +172,7 @@ pcap_findalldevs_interfaces(pcap_if_list_t *devlistp, char *errbuf, * Don't let the buffer size get bigger than INT_MAX. */ if (buf_size > INT_MAX) { - (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, "interface information requires more than %u bytes", INT_MAX); (void)close(fd); @@ -399,7 +393,7 @@ pcap_findalldevs_interfaces(pcap_if_list_t *devlistp, char *errbuf, * We have a ":"; is it followed by a number? */ q = p + 1; - while (isdigit((unsigned char)*q)) + while (PCAP_ISDIGIT(*q)) q++; if (*q == '\0') { /* diff --git a/fad-glifc.c b/fad-glifc.c index f22f56d73eb7..6b275eb3c840 100644 --- a/fad-glifc.c +++ b/fad-glifc.c @@ -50,7 +50,6 @@ struct rtentry; /* declarations in */ #include #include -#include #include #include #include @@ -311,7 +310,7 @@ pcap_findalldevs_interfaces(pcap_if_list_t *devlistp, char *errbuf, * We have a ":"; is it followed by a number? */ q = p + 1; - while (isdigit((unsigned char)*q)) + while (PCAP_ISDIGIT(*q)) q++; if (*q == '\0') { /* diff --git a/fmtutils.c b/fmtutils.c index 091e0d35eede..2d3576244107 100644 --- a/fmtutils.c +++ b/fmtutils.c @@ -47,12 +47,220 @@ #include #include -#include +#include "pcap-int.h" #include "portability.h" #include "fmtutils.h" +#ifdef _WIN32 +#include "charconv.h" +#endif + +/* + * Set the encoding. + */ +#ifdef _WIN32 +/* + * True if we shouold use UTF-8. + */ +static int use_utf_8; + +void +pcap_fmt_set_encoding(unsigned int opts) +{ + if (opts == PCAP_CHAR_ENC_UTF_8) + use_utf_8 = 1; +} +#else +void +pcap_fmt_set_encoding(unsigned int opts _U_) +{ + /* + * Nothing to do here. + */ +} +#endif + +#ifdef _WIN32 +/* + * Convert a null-terminated UTF-16LE string to UTF-8, putting it into + * a buffer starting at the specified location and stopping if we go + * past the specified size. This will only put out complete UTF-8 + * sequences. + * + * We do this ourselves because Microsoft doesn't offer a "convert and + * stop at a UTF-8 character boundary if we run out of space" routine. + */ +#define IS_LEADING_SURROGATE(c) \ + ((c) >= 0xd800 && (c) < 0xdc00) +#define IS_TRAILING_SURROGATE(c) \ + ((c) >= 0xdc00 && (c) < 0xe000) +#define SURROGATE_VALUE(leading, trailing) \ + (((((leading) - 0xd800) << 10) | ((trailing) - 0xdc00)) + 0x10000) +#define REPLACEMENT_CHARACTER 0x0FFFD + +static char * +utf_16le_to_utf_8_truncated(const wchar_t *utf_16, char *utf_8, + size_t utf_8_len) +{ + wchar_t c, c2; + uint32_t uc; + + if (utf_8_len == 0) { + /* + * Not even enough room for a trailing '\0'. + * Don't put anything into the buffer. + */ + return (utf_8); + } + + while ((c = *utf_16++) != '\0') { + if (IS_LEADING_SURROGATE(c)) { + /* + * Leading surrogate. Must be followed by + * a trailing surrogate. + */ + c2 = *utf_16; + if (c2 == '\0') { + /* + * Oops, string ends with a lead + * surrogate. Try to drop in + * a REPLACEMENT CHARACTER, and + * don't move the string pointer, + * so on the next trip through + * the loop we grab the terminating + * '\0' and quit. + */ + uc = REPLACEMENT_CHARACTER; + } else { + /* + * OK, we can consume this 2-octet + * value. + */ + utf_16++; + if (IS_TRAILING_SURROGATE(c2)) { + /* + * Trailing surrogate. + * This calculation will, + * for c being a leading + * surrogate and c2 being + * a trailing surrogate, + * produce a value between + * 0x100000 and 0x10ffff, + * so it's always going to be + * a valid Unicode code point. + */ + uc = SURROGATE_VALUE(c, c2); + } else { + /* + * Not a trailing surroage; + * try to drop in a + * REPLACEMENT CHARACTER. + */ + uc = REPLACEMENT_CHARACTER; + } + } + } else { + /* + * Not a leading surrogate. + */ + if (IS_TRAILING_SURROGATE(c)) { + /* + * Trailing surrogate without + * a preceding leading surrogate. + * Try to drop in a REPLACEMENT + * CHARACTER. + */ + uc = REPLACEMENT_CHARACTER; + } else { + /* + * This is a valid BMP character; + * drop it in. + */ + uc = c; + } + } + + /* + * OK, uc is a valid Unicode character; how + * many bytes worth of UTF-8 does it require? + */ + if (uc < 0x0080) { + /* 1 byte. */ + if (utf_8_len < 2) { + /* + * Not enough room for that byte + * plus a trailing '\0'. + */ + break; + } + *utf_8++ = (char)uc; + utf_8_len--; + } else if (uc < 0x0800) { + /* 2 bytes. */ + if (utf_8_len < 3) { + /* + * Not enough room for those bytes + * plus a trailing '\0'. + */ + break; + } + *utf_8++ = ((uc >> 6) & 0x3F) | 0xC0; + *utf_8++ = ((uc >> 0) & 0x3F) | 0x80; + utf_8_len -= 2; + } else if (uc < 0x010000) { + /* 3 bytes. */ + if (utf_8_len < 4) { + /* + * Not enough room for those bytes + * plus a trailing '\0'. + */ + break; + } + *utf_8++ = ((uc >> 12) & 0x0F) | 0xE0; + *utf_8++ = ((uc >> 6) & 0x3F) | 0x80; + *utf_8++ = ((uc >> 0) & 0x3F) | 0x80; + utf_8_len -= 3; + } else { + /* 4 bytes. */ + if (utf_8_len < 5) { + /* + * Not enough room for those bytes + * plus a trailing '\0'. + */ + break; + } + *utf_8++ = ((uc >> 18) & 0x03) | 0xF0; + *utf_8++ = ((uc >> 12) & 0x3F) | 0x80; + *utf_8++ = ((uc >> 6) & 0x3F) | 0x80; + *utf_8++ = ((uc >> 0) & 0x3F) | 0x80; + utf_8_len -= 3; + } + } + + /* + * OK, we have enough room for (at least) a trailing '\0'. + * (We started out with enough room, thanks to the test + * for a zero-length buffer at the beginning, and if + * there wasn't enough room for any character we wanted + * to put into the buffer *plus* a trailing '\0', + * we'd have quit before putting it into the buffer, + * and thus would have left enough room for the trailing + * '\0'.) + * + * Drop it in. + */ + *utf_8 = '\0'; + + /* + * Return a pointer to the terminating '\0', in case we + * want to drop something in after that. + */ + return (utf_8); +} +#endif /* _WIN32 */ + /* * Generate an error message based on a format, arguments, and an * errno, with a message for the errno after the formatted output. @@ -62,13 +270,21 @@ pcap_fmt_errmsg_for_errno(char *errbuf, size_t errbuflen, int errnum, const char *fmt, ...) { va_list ap; + + va_start(ap, fmt); + pcap_vfmt_errmsg_for_errno(errbuf, errbuflen, errnum, fmt, ap); + va_end(ap); +} + +void +pcap_vfmt_errmsg_for_errno(char *errbuf, size_t errbuflen, int errnum, + const char *fmt, va_list ap) +{ size_t msglen; char *p; size_t errbuflen_remaining; - va_start(ap, fmt); - pcap_vsnprintf(errbuf, errbuflen, fmt, ap); - va_end(ap); + (void)vsnprintf(errbuf, errbuflen, fmt, ap); msglen = strlen(errbuf); /* @@ -84,24 +300,40 @@ pcap_fmt_errmsg_for_errno(char *errbuf, size_t errbuflen, int errnum, *p++ = ':'; *p++ = ' '; *p = '\0'; - msglen += 2; errbuflen_remaining -= 2; /* * Now append the string for the error code. */ -#if defined(HAVE_STRERROR_S) +#if defined(HAVE__WCSERROR_S) /* - * We have a Windows-style strerror_s(). + * We have a Windows-style _wcserror_s(). + * Generate a UTF-16LE error message. */ - errno_t err = strerror_s(p, errbuflen_remaining, errnum); + wchar_t utf_16_errbuf[PCAP_ERRBUF_SIZE]; + errno_t err = _wcserror_s(utf_16_errbuf, PCAP_ERRBUF_SIZE, errnum); if (err != 0) { /* * It doesn't appear to be documented anywhere obvious - * what the error returns from strerror_s(). + * what the error returns from _wcserror_s(). */ - pcap_snprintf(p, errbuflen_remaining, "Error %d", errnum); + snprintf(p, errbuflen_remaining, "Error %d", errnum); + return; } + + /* + * Now convert it from UTF-16LE to UTF-8, dropping it in the + * remaining space in the buffer, and truncating it - cleanly, + * on a UTF-8 character boundary - if it doesn't fit. + */ + utf_16le_to_utf_8_truncated(utf_16_errbuf, p, errbuflen_remaining); + + /* + * Now, if we're not in UTF-8 mode, convert errbuf to the + * local code page. + */ + if (!use_utf_8) + utf_8_to_acp_truncated(errbuf); #elif defined(HAVE_GNU_STRERROR_R) /* * We have a GNU-style strerror_r(), which is *not* guaranteed to @@ -113,7 +345,7 @@ pcap_fmt_errmsg_for_errno(char *errbuf, size_t errbuflen, int errnum, */ char strerror_buf[PCAP_ERRBUF_SIZE]; char *errstring = strerror_r(errnum, strerror_buf, PCAP_ERRBUF_SIZE); - pcap_snprintf(p, errbuflen_remaining, "%s", errstring); + snprintf(p, errbuflen_remaining, "%s", errstring); #elif defined(HAVE_POSIX_STRERROR_R) /* * We have a POSIX-style strerror_r(), which is guaranteed to fill @@ -125,22 +357,22 @@ pcap_fmt_errmsg_for_errno(char *errbuf, size_t errbuflen, int errnum, * UNIX 03 says this isn't guaranteed to produce a * fallback error message. */ - pcap_snprintf(p, errbuflen_remaining, "Unknown error: %d", + snprintf(p, errbuflen_remaining, "Unknown error: %d", errnum); } else if (err == ERANGE) { /* * UNIX 03 says this isn't guaranteed to produce a * fallback error message. */ - pcap_snprintf(p, errbuflen_remaining, + snprintf(p, errbuflen_remaining, "Message for error %d is too long", errnum); } #else /* - * We have neither strerror_s() nor strerror_r(), so we're + * We have neither _wcserror_s() nor strerror_r(), so we're * stuck with using pcap_strerror(). */ - pcap_snprintf(p, errbuflen_remaining, "%s", pcap_strerror(errnum)); + snprintf(p, errbuflen_remaining, "%s", pcap_strerror(errnum)); #endif } @@ -154,15 +386,24 @@ pcap_fmt_errmsg_for_win32_err(char *errbuf, size_t errbuflen, DWORD errnum, const char *fmt, ...) { va_list ap; + + va_start(ap, fmt); + pcap_vfmt_errmsg_for_win32_err(errbuf, errbuflen, errnum, fmt, ap); + va_end(ap); +} + +void +pcap_vfmt_errmsg_for_win32_err(char *errbuf, size_t errbuflen, DWORD errnum, + const char *fmt, va_list ap) +{ size_t msglen; char *p; size_t errbuflen_remaining; DWORD retval; - char win32_errbuf[PCAP_ERRBUF_SIZE+1]; + wchar_t utf_16_errbuf[PCAP_ERRBUF_SIZE]; + size_t utf_8_len; - va_start(ap, fmt); - pcap_vsnprintf(errbuf, errbuflen, fmt, ap); - va_end(ap); + vsnprintf(errbuf, errbuflen, fmt, ap); msglen = strlen(errbuf); /* @@ -197,18 +438,39 @@ pcap_fmt_errmsg_for_win32_err(char *errbuf, size_t errbuflen, DWORD errnum, * get the message translated if it's in a language they don't * happen to understand. */ - retval = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS|FORMAT_MESSAGE_MAX_WIDTH_MASK, + retval = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS|FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL, errnum, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - win32_errbuf, PCAP_ERRBUF_SIZE, NULL); + utf_16_errbuf, PCAP_ERRBUF_SIZE, NULL); if (retval == 0) { /* * Failed. */ - pcap_snprintf(p, errbuflen_remaining, + snprintf(p, errbuflen_remaining, "Couldn't get error message for error (%lu)", errnum); return; } - pcap_snprintf(p, errbuflen_remaining, "%s (%lu)", win32_errbuf, errnum); + /* + * Now convert it from UTF-16LE to UTF-8. + */ + p = utf_16le_to_utf_8_truncated(utf_16_errbuf, p, errbuflen_remaining); + + /* + * Now append the error number, if it fits. + */ + utf_8_len = p - errbuf; + errbuflen_remaining -= utf_8_len; + if (utf_8_len == 0) { + /* The message was empty. */ + snprintf(p, errbuflen_remaining, "(%lu)", errnum); + } else + snprintf(p, errbuflen_remaining, " (%lu)", errnum); + + /* + * Now, if we're not in UTF-8 mode, convert errbuf to the + * local code page. + */ + if (!use_utf_8) + utf_8_to_acp_truncated(errbuf); } #endif diff --git a/fmtutils.h b/fmtutils.h index 838948bcd3d6..4fa344865205 100644 --- a/fmtutils.h +++ b/fmtutils.h @@ -34,18 +34,26 @@ #ifndef fmtutils_h #define fmtutils_h +#include /* we declare varargs functions */ + #include "pcap/funcattrs.h" #ifdef __cplusplus extern "C" { #endif +void pcap_fmt_set_encoding(unsigned int); + void pcap_fmt_errmsg_for_errno(char *, size_t, int, PCAP_FORMAT_STRING(const char *), ...) PCAP_PRINTFLIKE(4, 5); +void pcap_vfmt_errmsg_for_errno(char *, size_t, int, + PCAP_FORMAT_STRING(const char *), va_list) PCAP_PRINTFLIKE(4, 0); #ifdef _WIN32 void pcap_fmt_errmsg_for_win32_err(char *, size_t, DWORD, PCAP_FORMAT_STRING(const char *), ...) PCAP_PRINTFLIKE(4, 5); +void pcap_vfmt_errmsg_for_win32_err(char *, size_t, DWORD, + PCAP_FORMAT_STRING(const char *), va_list) PCAP_PRINTFLIKE(4, 0); #endif #ifdef __cplusplus diff --git a/ftmacros.h b/ftmacros.h index cd3daebdf175..7975463b3a69 100644 --- a/ftmacros.h +++ b/ftmacros.h @@ -45,7 +45,12 @@ * namespace to the maximum extent possible"? */ #if defined(sun) || defined(__sun) - #define __EXTENSIONS__ + /* + * On Solaris Clang defines __EXTENSIONS__ automatically. + */ + #ifndef __EXTENSIONS__ + #define __EXTENSIONS__ + #endif /* * We also need to define _XPG4_2 in order to get @@ -83,14 +88,18 @@ * least with HP's C compiler; hopefully doing so won't make it * *not* work with *un*-threaded code. */ -#elif defined(__linux__) || defined(linux) || defined(__linux) +#else /* * Turn on _GNU_SOURCE to get everything GNU libc has to offer, - * including asprintf(). + * including asprintf(), if we're using GNU libc. * * Unfortunately, one thing it has to offer is a strerror_r() * that's not POSIX-compliant, but we deal with that in * pcap_fmt_errmsg_for_errno(). + * + * We don't limit this to, for example, Linux and Cygwin, because + * this might, for example, be GNU/HURD or one of Debian's kFreeBSD + * OSes ("GNU/FreeBSD"). */ #define _GNU_SOURCE @@ -101,9 +110,18 @@ * don't whine about _BSD_SOURCE being deprecated; we still have * to define _BSD_SOURCE to handle older versions of GNU libc that * don't support _DEFAULT_SOURCE. + * + * But, if it's already defined, don't define it, so that we don't + * get a warning of it being redefined if it's defined as, for + * example, 1. */ - #define _DEFAULT_SOURCE - #define _BSD_SOURCE + #ifndef _DEFAULT_SOURCE + #define _DEFAULT_SOURCE + #endif + /* Avoid redefining _BSD_SOURCE if it's already defined as for ex. 1 */ + #ifndef _BSD_SOURCE + #define _BSD_SOURCE + #endif #endif #endif diff --git a/gencode.c b/gencode.c index 3fc97775daed..87a6e962b012 100644 --- a/gencode.c +++ b/gencode.c @@ -1,4 +1,3 @@ -/*#define CHASE_CHAIN*/ /* * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998 * The Regents of the University of California. All rights reserved. @@ -24,7 +23,6 @@ #include #endif -#include #ifdef _WIN32 #include #else @@ -43,6 +41,7 @@ #include #include #include +#include #ifdef MSDOS #include "pcap-dos.h" @@ -59,27 +58,21 @@ #include "ieee80211.h" #include "atmuni31.h" #include "sunatmpos.h" +#include "pflog.h" #include "ppp.h" #include "pcap/sll.h" #include "pcap/ipnet.h" #include "arcnet.h" +#include "diag-control.h" -#include "grammar.h" #include "scanner.h" -#if defined(linux) && defined(PF_PACKET) && defined(SO_ATTACH_FILTER) +#if defined(linux) #include #include #include #endif -#ifdef HAVE_NET_PFVAR_H -#include -#include -#include -#include -#endif - #ifndef offsetof #define offsetof(s, e) ((size_t)&((s *)0)->e) #endif @@ -139,10 +132,6 @@ struct addrinfo { #define ETHERMTU 1500 -#ifndef ETHERTYPE_TEB -#define ETHERTYPE_TEB 0x6558 -#endif - #ifndef IPPROTO_HOPOPTS #define IPPROTO_HOPOPTS 0 #endif @@ -248,6 +237,7 @@ struct chunk { struct _compiler_state { jmp_buf top_ctx; pcap_t *bpf_pcap; + int error_set; struct icode ic; @@ -435,10 +425,22 @@ bpf_set_error(compiler_state_t *cstate, const char *fmt, ...) { va_list ap; - va_start(ap, fmt); - (void)pcap_vsnprintf(cstate->bpf_pcap->errbuf, PCAP_ERRBUF_SIZE, - fmt, ap); - va_end(ap); + /* + * If we've already set an error, don't override it. + * The lexical analyzer reports some errors by setting + * the error and then returning a LEX_ERROR token, which + * is not recognized by any grammar rule, and thus forces + * the parse to stop. We don't want the error reported + * by the lexical analyzer to be overwritten by the syntax + * error. + */ + if (!cstate->error_set) { + va_start(ap, fmt); + (void)vsnprintf(cstate->bpf_pcap->errbuf, PCAP_ERRBUF_SIZE, + fmt, ap); + va_end(ap); + cstate->error_set = 1; + } } /* @@ -454,11 +456,14 @@ bpf_error(compiler_state_t *cstate, const char *fmt, ...) va_list ap; va_start(ap, fmt); - (void)pcap_vsnprintf(cstate->bpf_pcap->errbuf, PCAP_ERRBUF_SIZE, + (void)vsnprintf(cstate->bpf_pcap->errbuf, PCAP_ERRBUF_SIZE, fmt, ap); va_end(ap); longjmp(cstate->top_ctx, 1); /*NOTREACHED*/ +#ifdef _AIX + PCAP_UNREACHABLE +#endif /* _AIX */ } static int init_linktype(compiler_state_t *, pcap_t *); @@ -479,21 +484,21 @@ static inline void syntax(compiler_state_t *cstate); static void backpatch(struct block *, struct block *); static void merge(struct block *, struct block *); static struct block *gen_cmp(compiler_state_t *, enum e_offrel, u_int, - u_int, bpf_int32); + u_int, bpf_u_int32); static struct block *gen_cmp_gt(compiler_state_t *, enum e_offrel, u_int, - u_int, bpf_int32); + u_int, bpf_u_int32); static struct block *gen_cmp_ge(compiler_state_t *, enum e_offrel, u_int, - u_int, bpf_int32); + u_int, bpf_u_int32); static struct block *gen_cmp_lt(compiler_state_t *, enum e_offrel, u_int, - u_int, bpf_int32); + u_int, bpf_u_int32); static struct block *gen_cmp_le(compiler_state_t *, enum e_offrel, u_int, - u_int, bpf_int32); + u_int, bpf_u_int32); static struct block *gen_mcmp(compiler_state_t *, enum e_offrel, u_int, - u_int, bpf_int32, bpf_u_int32); + u_int, bpf_u_int32, bpf_u_int32); static struct block *gen_bcmp(compiler_state_t *, enum e_offrel, u_int, u_int, const u_char *); -static struct block *gen_ncmp(compiler_state_t *, enum e_offrel, bpf_u_int32, - bpf_u_int32, bpf_u_int32, bpf_u_int32, int, bpf_int32); +static struct block *gen_ncmp(compiler_state_t *, enum e_offrel, u_int, + u_int, bpf_u_int32, int, int, bpf_u_int32); static struct slist *gen_load_absoffsetrel(compiler_state_t *, bpf_abs_offset *, u_int, u_int); static struct slist *gen_load_a(compiler_state_t *, enum e_offrel, u_int, @@ -502,9 +507,10 @@ static struct slist *gen_loadx_iphdrlen(compiler_state_t *); static struct block *gen_uncond(compiler_state_t *, int); static inline struct block *gen_true(compiler_state_t *); static inline struct block *gen_false(compiler_state_t *); -static struct block *gen_ether_linktype(compiler_state_t *, int); -static struct block *gen_ipnet_linktype(compiler_state_t *, int); -static struct block *gen_linux_sll_linktype(compiler_state_t *, int); +static struct block *gen_ether_linktype(compiler_state_t *, bpf_u_int32); +static struct block *gen_ipnet_linktype(compiler_state_t *, bpf_u_int32); +static struct block *gen_linux_sll_linktype(compiler_state_t *, bpf_u_int32); +static struct slist *gen_load_pflog_llprefixlen(compiler_state_t *); static struct slist *gen_load_prism_llprefixlen(compiler_state_t *); static struct slist *gen_load_avs_llprefixlen(compiler_state_t *); static struct slist *gen_load_radiotap_llprefixlen(compiler_state_t *); @@ -512,15 +518,15 @@ static struct slist *gen_load_ppi_llprefixlen(compiler_state_t *); static void insert_compute_vloffsets(compiler_state_t *, struct block *); static struct slist *gen_abs_offset_varpart(compiler_state_t *, bpf_abs_offset *); -static int ethertype_to_ppptype(int); -static struct block *gen_linktype(compiler_state_t *, int); +static bpf_u_int32 ethertype_to_ppptype(bpf_u_int32); +static struct block *gen_linktype(compiler_state_t *, bpf_u_int32); static struct block *gen_snap(compiler_state_t *, bpf_u_int32, bpf_u_int32); -static struct block *gen_llc_linktype(compiler_state_t *, int); +static struct block *gen_llc_linktype(compiler_state_t *, bpf_u_int32); static struct block *gen_hostop(compiler_state_t *, bpf_u_int32, bpf_u_int32, - int, int, u_int, u_int); + int, bpf_u_int32, u_int, u_int); #ifdef INET6 static struct block *gen_hostop6(compiler_state_t *, struct in6_addr *, - struct in6_addr *, int, int, u_int, u_int); + struct in6_addr *, int, bpf_u_int32, u_int, u_int); #endif static struct block *gen_ahostop(compiler_state_t *, const u_char *, int); static struct block *gen_ehostop(compiler_state_t *, const u_char *, int); @@ -529,7 +535,7 @@ static struct block *gen_thostop(compiler_state_t *, const u_char *, int); static struct block *gen_wlanhostop(compiler_state_t *, const u_char *, int); static struct block *gen_ipfchostop(compiler_state_t *, const u_char *, int); static struct block *gen_dnhostop(compiler_state_t *, bpf_u_int32, int); -static struct block *gen_mpls_linktype(compiler_state_t *, int); +static struct block *gen_mpls_linktype(compiler_state_t *, bpf_u_int32); static struct block *gen_host(compiler_state_t *, bpf_u_int32, bpf_u_int32, int, int, int); #ifdef INET6 @@ -541,23 +547,27 @@ static struct block *gen_gateway(compiler_state_t *, const u_char *, struct addrinfo *, int, int); #endif static struct block *gen_ipfrag(compiler_state_t *); -static struct block *gen_portatom(compiler_state_t *, int, bpf_int32); -static struct block *gen_portrangeatom(compiler_state_t *, int, bpf_int32, - bpf_int32); -static struct block *gen_portatom6(compiler_state_t *, int, bpf_int32); -static struct block *gen_portrangeatom6(compiler_state_t *, int, bpf_int32, - bpf_int32); -struct block *gen_portop(compiler_state_t *, int, int, int); -static struct block *gen_port(compiler_state_t *, int, int, int); -struct block *gen_portrangeop(compiler_state_t *, int, int, int, int); -static struct block *gen_portrange(compiler_state_t *, int, int, int, int); -struct block *gen_portop6(compiler_state_t *, int, int, int); -static struct block *gen_port6(compiler_state_t *, int, int, int); -struct block *gen_portrangeop6(compiler_state_t *, int, int, int, int); -static struct block *gen_portrange6(compiler_state_t *, int, int, int, int); +static struct block *gen_portatom(compiler_state_t *, int, bpf_u_int32); +static struct block *gen_portrangeatom(compiler_state_t *, u_int, bpf_u_int32, + bpf_u_int32); +static struct block *gen_portatom6(compiler_state_t *, int, bpf_u_int32); +static struct block *gen_portrangeatom6(compiler_state_t *, u_int, bpf_u_int32, + bpf_u_int32); +static struct block *gen_portop(compiler_state_t *, u_int, u_int, int); +static struct block *gen_port(compiler_state_t *, u_int, int, int); +static struct block *gen_portrangeop(compiler_state_t *, u_int, u_int, + bpf_u_int32, int); +static struct block *gen_portrange(compiler_state_t *, u_int, u_int, int, int); +struct block *gen_portop6(compiler_state_t *, u_int, u_int, int); +static struct block *gen_port6(compiler_state_t *, u_int, int, int); +static struct block *gen_portrangeop6(compiler_state_t *, u_int, u_int, + bpf_u_int32, int); +static struct block *gen_portrange6(compiler_state_t *, u_int, u_int, int, int); static int lookup_proto(compiler_state_t *, const char *, int); -static struct block *gen_protochain(compiler_state_t *, int, int, int); -static struct block *gen_proto(compiler_state_t *, int, int, int); +#if !defined(NO_PROTOCHAIN) +static struct block *gen_protochain(compiler_state_t *, bpf_u_int32, int); +#endif /* !defined(NO_PROTOCHAIN) */ +static struct block *gen_proto(compiler_state_t *, bpf_u_int32, int, int); static struct slist *xfer_to_x(compiler_state_t *, struct arth *); static struct slist *xfer_to_a(compiler_state_t *, struct arth *); static struct block *gen_mac_multicast(compiler_state_t *, int); @@ -567,7 +577,7 @@ static struct block *gen_geneve_ll_check(compiler_state_t *cstate); static struct block *gen_ppi_dlt_check(compiler_state_t *); static struct block *gen_atmfield_code_internal(compiler_state_t *, int, - bpf_int32, bpf_u_int32, int); + bpf_u_int32, int, int); static struct block *gen_atmtype_llc(compiler_state_t *); static struct block *gen_msg_abbrev(compiler_state_t *, int type); @@ -721,9 +731,9 @@ pcap_compile(pcap_t *p, struct bpf_program *program, * link-layer type, so we can't use it. */ if (!p->activated) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "not-yet-activated pcap_t passed to pcap_compile"); - return (-1); + return (PCAP_ERROR); } #ifdef _WIN32 @@ -762,15 +772,16 @@ pcap_compile(pcap_t *p, struct bpf_program *program, cstate.ic.root = NULL; cstate.ic.cur_mark = 0; cstate.bpf_pcap = p; + cstate.error_set = 0; init_regs(&cstate); cstate.netmask = mask; cstate.snaplen = pcap_snapshot(p); if (cstate.snaplen == 0) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "snaplen of 0 rejects all packets"); - rc = -1; + rc = PCAP_ERROR; goto quit; } @@ -786,7 +797,7 @@ pcap_compile(pcap_t *p, struct bpf_program *program, pcap_set_extra(&cstate, scanner); if (init_linktype(&cstate, p) == -1) { - rc = -1; + rc = PCAP_ERROR; goto quit; } if (pcap_parse(scanner, &cstate) != 0) { @@ -796,7 +807,7 @@ pcap_compile(pcap_t *p, struct bpf_program *program, #endif if (cstate.e != NULL) free(cstate.e); - rc = -1; + rc = PCAP_ERROR; goto quit; } @@ -805,7 +816,7 @@ pcap_compile(pcap_t *p, struct bpf_program *program, * Catch errors reported by gen_retblk(). */ if (setjmp(cstate.top_ctx)) { - rc = -1; + rc = PCAP_ERROR; goto quit; } cstate.ic.root = gen_retblk(&cstate, cstate.snaplen); @@ -814,14 +825,14 @@ pcap_compile(pcap_t *p, struct bpf_program *program, if (optimize && !cstate.no_optimize) { if (bpf_optimize(&cstate.ic, p->errbuf) == -1) { /* Failure */ - rc = -1; + rc = PCAP_ERROR; goto quit; } if (cstate.ic.root == NULL || (cstate.ic.root->s.code == (BPF_RET|BPF_K) && cstate.ic.root->s.k == 0)) { - (void)pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + (void)snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "expression rejects all packets"); - rc = -1; + rc = PCAP_ERROR; goto quit; } } @@ -829,7 +840,7 @@ pcap_compile(pcap_t *p, struct bpf_program *program, cstate.ic.root, &len, p->errbuf); if (program->bf_insns == NULL) { /* Failure */ - rc = -1; + rc = PCAP_ERROR; goto quit; } program->bf_len = len; @@ -867,7 +878,7 @@ pcap_compile_nopcap(int snaplen_arg, int linktype_arg, p = pcap_open_dead(linktype_arg, snaplen_arg); if (p == NULL) - return (-1); + return (PCAP_ERROR); ret = pcap_compile(p, program, buf, optimize, mask); pcap_close(p); return (ret); @@ -1013,42 +1024,42 @@ gen_not(struct block *b) static struct block * gen_cmp(compiler_state_t *cstate, enum e_offrel offrel, u_int offset, - u_int size, bpf_int32 v) + u_int size, bpf_u_int32 v) { return gen_ncmp(cstate, offrel, offset, size, 0xffffffff, BPF_JEQ, 0, v); } static struct block * gen_cmp_gt(compiler_state_t *cstate, enum e_offrel offrel, u_int offset, - u_int size, bpf_int32 v) + u_int size, bpf_u_int32 v) { return gen_ncmp(cstate, offrel, offset, size, 0xffffffff, BPF_JGT, 0, v); } static struct block * gen_cmp_ge(compiler_state_t *cstate, enum e_offrel offrel, u_int offset, - u_int size, bpf_int32 v) + u_int size, bpf_u_int32 v) { return gen_ncmp(cstate, offrel, offset, size, 0xffffffff, BPF_JGE, 0, v); } static struct block * gen_cmp_lt(compiler_state_t *cstate, enum e_offrel offrel, u_int offset, - u_int size, bpf_int32 v) + u_int size, bpf_u_int32 v) { return gen_ncmp(cstate, offrel, offset, size, 0xffffffff, BPF_JGE, 1, v); } static struct block * gen_cmp_le(compiler_state_t *cstate, enum e_offrel offrel, u_int offset, - u_int size, bpf_int32 v) + u_int size, bpf_u_int32 v) { return gen_ncmp(cstate, offrel, offset, size, 0xffffffff, BPF_JGT, 1, v); } static struct block * gen_mcmp(compiler_state_t *cstate, enum e_offrel offrel, u_int offset, - u_int size, bpf_int32 v, bpf_u_int32 mask) + u_int size, bpf_u_int32 v, bpf_u_int32 mask) { return gen_ncmp(cstate, offrel, offset, size, mask, BPF_JEQ, 0, v); } @@ -1059,22 +1070,12 @@ gen_bcmp(compiler_state_t *cstate, enum e_offrel offrel, u_int offset, { register struct block *b, *tmp; - /* - * XXX - the actual *instructions* do unsigned comparisons on - * most platforms, and the load instructions don't do sign - * extension, so gen_cmp() should really take an unsigned - * value argument. - * - * As the load instructons also don't do sign-extension, we - * fetch the values from the byte array as unsigned. We don't - * want to use the signed versions of the extract calls. - */ b = NULL; while (size >= 4) { register const u_char *p = &v[size - 4]; tmp = gen_cmp(cstate, offrel, offset + size - 4, BPF_W, - (bpf_int32)EXTRACT_32BITS(p)); + EXTRACT_BE_U_4(p)); if (b != NULL) gen_and(b, tmp); b = tmp; @@ -1084,14 +1085,14 @@ gen_bcmp(compiler_state_t *cstate, enum e_offrel offrel, u_int offset, register const u_char *p = &v[size - 2]; tmp = gen_cmp(cstate, offrel, offset + size - 2, BPF_H, - (bpf_int32)EXTRACT_16BITS(p)); + EXTRACT_BE_U_2(p)); if (b != NULL) gen_and(b, tmp); b = tmp; size -= 2; } if (size > 0) { - tmp = gen_cmp(cstate, offrel, offset, BPF_B, (bpf_int32)v[0]); + tmp = gen_cmp(cstate, offrel, offset, BPF_B, v[0]); if (b != NULL) gen_and(b, tmp); b = tmp; @@ -1106,9 +1107,9 @@ gen_bcmp(compiler_state_t *cstate, enum e_offrel offrel, u_int offset, * should test the opposite of "jtype". */ static struct block * -gen_ncmp(compiler_state_t *cstate, enum e_offrel offrel, bpf_u_int32 offset, - bpf_u_int32 size, bpf_u_int32 mask, bpf_u_int32 jtype, int reverse, - bpf_int32 v) +gen_ncmp(compiler_state_t *cstate, enum e_offrel offrel, u_int offset, + u_int size, bpf_u_int32 mask, int jtype, int reverse, + bpf_u_int32 v) { struct slist *s, *s2; struct block *b; @@ -1256,6 +1257,7 @@ init_linktype(compiler_state_t *cstate, pcap_t *p) case DLT_PPP: case DLT_PPP_PPPD: case DLT_C_HDLC: /* BSD/OS Cisco HDLC */ + case DLT_HDLC: /* NetBSD (Cisco) HDLC */ case DLT_PPP_SERIAL: /* NetBSD sync/async serial PPP */ cstate->off_linktype.constant_part = 2; /* skip HDLC-like framing */ cstate->off_linkpl.constant_part = 4; /* skip HDLC-like framing and protocol field */ @@ -1504,14 +1506,13 @@ init_linktype(compiler_state_t *cstate, pcap_t *p) cstate->off_nl_nosnap = 0; /* XXX - what does it do with 802.3 packets? */ break; -#ifdef HAVE_NET_PFVAR_H case DLT_PFLOG: cstate->off_linktype.constant_part = 0; - cstate->off_linkpl.constant_part = PFLOG_HDRLEN; + cstate->off_linkpl.constant_part = 0; /* link-layer header is variable-length */ + cstate->off_linkpl.is_variable = 1; cstate->off_nl = 0; cstate->off_nl_nosnap = 0; /* no 802.2 LLC */ break; -#endif case DLT_JUNIPER_MFR: case DLT_JUNIPER_MLFR: @@ -1713,7 +1714,8 @@ init_linktype(compiler_state_t *cstate, pcap_t *p) cstate->off_nl = OFFSET_NOT_SET; cstate->off_nl_nosnap = OFFSET_NOT_SET; } else { - bpf_set_error(cstate, "unknown data link type %d", cstate->linktype); + bpf_set_error(cstate, "unknown data link type %d (min %d, max %d)", + cstate->linktype, DLT_MATCHING_MIN, DLT_MATCHING_MAX); return (-1); } break; @@ -1956,11 +1958,11 @@ gen_false(compiler_state_t *cstate) * the appropriate test. */ static struct block * -gen_ether_linktype(compiler_state_t *cstate, int proto) +gen_ether_linktype(compiler_state_t *cstate, bpf_u_int32 ll_proto) { struct block *b0, *b1; - switch (proto) { + switch (ll_proto) { case LLCSAP_ISONS: case LLCSAP_IP: @@ -1979,8 +1981,7 @@ gen_ether_linktype(compiler_state_t *cstate, int proto) */ b0 = gen_cmp_gt(cstate, OR_LINKTYPE, 0, BPF_H, ETHERMTU); gen_not(b0); - b1 = gen_cmp(cstate, OR_LLC, 0, BPF_H, (bpf_int32) - ((proto << 8) | proto)); + b1 = gen_cmp(cstate, OR_LLC, 0, BPF_H, (ll_proto << 8) | ll_proto); gen_and(b0, b1); return b1; @@ -2017,8 +2018,8 @@ gen_ether_linktype(compiler_state_t *cstate, int proto) * This generates code to check both for the * IPX LSAP (Ethernet_802.2) and for Ethernet_802.3. */ - b0 = gen_cmp(cstate, OR_LLC, 0, BPF_B, (bpf_int32)LLCSAP_IPX); - b1 = gen_cmp(cstate, OR_LLC, 0, BPF_H, (bpf_int32)0xFFFF); + b0 = gen_cmp(cstate, OR_LLC, 0, BPF_B, LLCSAP_IPX); + b1 = gen_cmp(cstate, OR_LLC, 0, BPF_H, 0xFFFF); gen_or(b0, b1); /* @@ -2048,7 +2049,7 @@ gen_ether_linktype(compiler_state_t *cstate, int proto) * do that before checking for the other frame * types. */ - b0 = gen_cmp(cstate, OR_LINKTYPE, 0, BPF_H, (bpf_int32)ETHERTYPE_IPX); + b0 = gen_cmp(cstate, OR_LINKTYPE, 0, BPF_H, ETHERTYPE_IPX); gen_or(b0, b1); return b1; @@ -2078,9 +2079,9 @@ gen_ether_linktype(compiler_state_t *cstate, int proto) * 0x000000 (encapsulated Ethernet) and a protocol * type of ETHERTYPE_AARP (Appletalk ARP). */ - if (proto == ETHERTYPE_ATALK) + if (ll_proto == ETHERTYPE_ATALK) b1 = gen_snap(cstate, 0x080007, ETHERTYPE_ATALK); - else /* proto == ETHERTYPE_AARP */ + else /* ll_proto == ETHERTYPE_AARP */ b1 = gen_snap(cstate, 0x000000, ETHERTYPE_AARP); gen_and(b0, b1); @@ -2089,13 +2090,13 @@ gen_ether_linktype(compiler_state_t *cstate, int proto) * phase 1?); we just check for the Ethernet * protocol type. */ - b0 = gen_cmp(cstate, OR_LINKTYPE, 0, BPF_H, (bpf_int32)proto); + b0 = gen_cmp(cstate, OR_LINKTYPE, 0, BPF_H, ll_proto); gen_or(b0, b1); return b1; default: - if (proto <= ETHERMTU) { + if (ll_proto <= ETHERMTU) { /* * This is an LLC SAP value, so the frames * that match would be 802.2 frames. @@ -2106,7 +2107,7 @@ gen_ether_linktype(compiler_state_t *cstate, int proto) */ b0 = gen_cmp_gt(cstate, OR_LINKTYPE, 0, BPF_H, ETHERMTU); gen_not(b0); - b1 = gen_cmp(cstate, OR_LINKTYPE, 2, BPF_B, (bpf_int32)proto); + b1 = gen_cmp(cstate, OR_LINKTYPE, 2, BPF_B, ll_proto); gen_and(b0, b1); return b1; } else { @@ -2115,18 +2116,17 @@ gen_ether_linktype(compiler_state_t *cstate, int proto) * the length/type field with it (if * the frame is an 802.2 frame, the length * field will be <= ETHERMTU, and, as - * "proto" is > ETHERMTU, this test + * "ll_proto" is > ETHERMTU, this test * will fail and the frame won't match, * which is what we want). */ - return gen_cmp(cstate, OR_LINKTYPE, 0, BPF_H, - (bpf_int32)proto); + return gen_cmp(cstate, OR_LINKTYPE, 0, BPF_H, ll_proto); } } } static struct block * -gen_loopback_linktype(compiler_state_t *cstate, int proto) +gen_loopback_linktype(compiler_state_t *cstate, bpf_u_int32 ll_proto) { /* * For DLT_NULL, the link-layer header is a 32-bit word @@ -2154,10 +2154,10 @@ gen_loopback_linktype(compiler_state_t *cstate, int proto) * code to compare against the result. */ if (cstate->bpf_pcap->rfile != NULL && cstate->bpf_pcap->swapped) - proto = SWAPLONG(proto); - proto = htonl(proto); + ll_proto = SWAPLONG(ll_proto); + ll_proto = htonl(ll_proto); } - return (gen_cmp(cstate, OR_LINKHDR, 0, BPF_W, (bpf_int32)proto)); + return (gen_cmp(cstate, OR_LINKHDR, 0, BPF_W, ll_proto)); } /* @@ -2165,17 +2165,16 @@ gen_loopback_linktype(compiler_state_t *cstate, int proto) * or IPv6 then we have an error. */ static struct block * -gen_ipnet_linktype(compiler_state_t *cstate, int proto) +gen_ipnet_linktype(compiler_state_t *cstate, bpf_u_int32 ll_proto) { - switch (proto) { + switch (ll_proto) { case ETHERTYPE_IP: - return gen_cmp(cstate, OR_LINKTYPE, 0, BPF_B, (bpf_int32)IPH_AF_INET); + return gen_cmp(cstate, OR_LINKTYPE, 0, BPF_B, IPH_AF_INET); /*NOTREACHED*/ case ETHERTYPE_IPV6: - return gen_cmp(cstate, OR_LINKTYPE, 0, BPF_B, - (bpf_int32)IPH_AF_INET6); + return gen_cmp(cstate, OR_LINKTYPE, 0, BPF_B, IPH_AF_INET6); /*NOTREACHED*/ default: @@ -2188,17 +2187,17 @@ gen_ipnet_linktype(compiler_state_t *cstate, int proto) /* * Generate code to match a particular packet type. * - * "proto" is an Ethernet type value, if > ETHERMTU, or an LLC SAP + * "ll_proto" is an Ethernet type value, if > ETHERMTU, or an LLC SAP * value, if <= ETHERMTU. We use that to determine whether to * match the type field or to check the type field for the special * LINUX_SLL_P_802_2 value and then do the appropriate test. */ static struct block * -gen_linux_sll_linktype(compiler_state_t *cstate, int proto) +gen_linux_sll_linktype(compiler_state_t *cstate, bpf_u_int32 ll_proto) { struct block *b0, *b1; - switch (proto) { + switch (ll_proto) { case LLCSAP_ISONS: case LLCSAP_IP: @@ -2216,8 +2215,7 @@ gen_linux_sll_linktype(compiler_state_t *cstate, int proto) * (i.e., other SAP values)? */ b0 = gen_cmp(cstate, OR_LINKTYPE, 0, BPF_H, LINUX_SLL_P_802_2); - b1 = gen_cmp(cstate, OR_LLC, 0, BPF_H, (bpf_int32) - ((proto << 8) | proto)); + b1 = gen_cmp(cstate, OR_LLC, 0, BPF_H, (ll_proto << 8) | ll_proto); gen_and(b0, b1); return b1; @@ -2247,7 +2245,7 @@ gen_linux_sll_linktype(compiler_state_t *cstate, int proto) * then put a check for LINUX_SLL_P_802_2 frames * before it. */ - b0 = gen_cmp(cstate, OR_LLC, 0, BPF_B, (bpf_int32)LLCSAP_IPX); + b0 = gen_cmp(cstate, OR_LLC, 0, BPF_B, LLCSAP_IPX); b1 = gen_snap(cstate, 0x000000, ETHERTYPE_IPX); gen_or(b0, b1); b0 = gen_cmp(cstate, OR_LINKTYPE, 0, BPF_H, LINUX_SLL_P_802_2); @@ -2265,7 +2263,7 @@ gen_linux_sll_linktype(compiler_state_t *cstate, int proto) * do that before checking for the other frame * types. */ - b0 = gen_cmp(cstate, OR_LINKTYPE, 0, BPF_H, (bpf_int32)ETHERTYPE_IPX); + b0 = gen_cmp(cstate, OR_LINKTYPE, 0, BPF_H, ETHERTYPE_IPX); gen_or(b0, b1); return b1; @@ -2294,9 +2292,9 @@ gen_linux_sll_linktype(compiler_state_t *cstate, int proto) * 0x000000 (encapsulated Ethernet) and a protocol * type of ETHERTYPE_AARP (Appletalk ARP). */ - if (proto == ETHERTYPE_ATALK) + if (ll_proto == ETHERTYPE_ATALK) b1 = gen_snap(cstate, 0x080007, ETHERTYPE_ATALK); - else /* proto == ETHERTYPE_AARP */ + else /* ll_proto == ETHERTYPE_AARP */ b1 = gen_snap(cstate, 0x000000, ETHERTYPE_AARP); gen_and(b0, b1); @@ -2305,13 +2303,13 @@ gen_linux_sll_linktype(compiler_state_t *cstate, int proto) * phase 1?); we just check for the Ethernet * protocol type. */ - b0 = gen_cmp(cstate, OR_LINKTYPE, 0, BPF_H, (bpf_int32)proto); + b0 = gen_cmp(cstate, OR_LINKTYPE, 0, BPF_H, ll_proto); gen_or(b0, b1); return b1; default: - if (proto <= ETHERMTU) { + if (ll_proto <= ETHERMTU) { /* * This is an LLC SAP value, so the frames * that match would be 802.2 frames. @@ -2321,7 +2319,7 @@ gen_linux_sll_linktype(compiler_state_t *cstate, int proto) */ b0 = gen_cmp(cstate, OR_LINKTYPE, 0, BPF_H, LINUX_SLL_P_802_2); b1 = gen_cmp(cstate, OR_LINKHDR, cstate->off_linkpl.constant_part, BPF_B, - (bpf_int32)proto); + ll_proto); gen_and(b0, b1); return b1; } else { @@ -2330,15 +2328,68 @@ gen_linux_sll_linktype(compiler_state_t *cstate, int proto) * the length/type field with it (if * the frame is an 802.2 frame, the length * field will be <= ETHERMTU, and, as - * "proto" is > ETHERMTU, this test + * "ll_proto" is > ETHERMTU, this test * will fail and the frame won't match, * which is what we want). */ - return gen_cmp(cstate, OR_LINKTYPE, 0, BPF_H, (bpf_int32)proto); + return gen_cmp(cstate, OR_LINKTYPE, 0, BPF_H, ll_proto); } } } +/* + * Load a value relative to the beginning of the link-layer header after the + * pflog header. + */ +static struct slist * +gen_load_pflog_llprefixlen(compiler_state_t *cstate) +{ + struct slist *s1, *s2; + + /* + * Generate code to load the length of the pflog header into + * the register assigned to hold that length, if one has been + * assigned. (If one hasn't been assigned, no code we've + * generated uses that prefix, so we don't need to generate any + * code to load it.) + */ + if (cstate->off_linkpl.reg != -1) { + /* + * The length is in the first byte of the header. + */ + s1 = new_stmt(cstate, BPF_LD|BPF_B|BPF_ABS); + s1->s.k = 0; + + /* + * Round it up to a multiple of 4. + * Add 3, and clear the lower 2 bits. + */ + s2 = new_stmt(cstate, BPF_ALU|BPF_ADD|BPF_K); + s2->s.k = 3; + sappend(s1, s2); + s2 = new_stmt(cstate, BPF_ALU|BPF_AND|BPF_K); + s2->s.k = 0xfffffffc; + sappend(s1, s2); + + /* + * Now allocate a register to hold that value and store + * it. + */ + s2 = new_stmt(cstate, BPF_ST); + s2->s.k = cstate->off_linkpl.reg; + sappend(s1, s2); + + /* + * Now move it into the X register. + */ + s2 = new_stmt(cstate, BPF_MISC|BPF_TAX); + sappend(s1, s2); + + return (s1); + } else + return (NULL); +} + static struct slist * gen_load_prism_llprefixlen(compiler_state_t *cstate) { @@ -2848,7 +2899,7 @@ gen_load_802_11_header_len(compiler_state_t *cstate, struct slist *s, struct sli s2->s.k = 3; sappend(s, s2); s2 = new_stmt(cstate, BPF_ALU|BPF_AND|BPF_IMM); - s2->s.k = ~3; + s2->s.k = (bpf_u_int32)~3; sappend(s, s2); s2 = new_stmt(cstate, BPF_ST); s2->s.k = cstate->off_linkpl.reg; @@ -2926,10 +2977,14 @@ insert_compute_vloffsets(compiler_state_t *cstate, struct block *b) case DLT_PPI: s = gen_load_802_11_header_len(cstate, s, b->stmts); break; + + case DLT_PFLOG: + s = gen_load_pflog_llprefixlen(cstate); + break; } /* - * If there there is no initialization yet and we need variable + * If there is no initialization yet and we need variable * length offsets for VLAN, initialize them to zero */ if (s == NULL && cstate->is_vlan_vloffset) { @@ -3035,33 +3090,33 @@ gen_abs_offset_varpart(compiler_state_t *cstate, bpf_abs_offset *off) /* * Map an Ethernet type to the equivalent PPP type. */ -static int -ethertype_to_ppptype(int proto) +static bpf_u_int32 +ethertype_to_ppptype(bpf_u_int32 ll_proto) { - switch (proto) { + switch (ll_proto) { case ETHERTYPE_IP: - proto = PPP_IP; + ll_proto = PPP_IP; break; case ETHERTYPE_IPV6: - proto = PPP_IPV6; + ll_proto = PPP_IPV6; break; case ETHERTYPE_DN: - proto = PPP_DECNET; + ll_proto = PPP_DECNET; break; case ETHERTYPE_ATALK: - proto = PPP_APPLE; + ll_proto = PPP_APPLE; break; case ETHERTYPE_NS: - proto = PPP_NS; + ll_proto = PPP_NS; break; case LLCSAP_ISONS: - proto = PPP_OSI; + ll_proto = PPP_OSI; break; case LLCSAP_8021D: @@ -3070,14 +3125,14 @@ ethertype_to_ppptype(int proto) * over PPP are Spanning Tree Protocol * Bridging PDUs. */ - proto = PPP_BRPDU; + ll_proto = PPP_BRPDU; break; case LLCSAP_IPX: - proto = PPP_IPX; + ll_proto = PPP_IPX; break; } - return (proto); + return (ll_proto); } /* @@ -3133,29 +3188,14 @@ gen_prevlinkhdr_check(compiler_state_t *cstate) * value, if <= ETHERMTU. */ static struct block * -gen_linktype(compiler_state_t *cstate, int proto) +gen_linktype(compiler_state_t *cstate, bpf_u_int32 ll_proto) { struct block *b0, *b1, *b2; const char *description; /* are we checking MPLS-encapsulated packets? */ - if (cstate->label_stack_depth > 0) { - switch (proto) { - case ETHERTYPE_IP: - case PPP_IP: - /* FIXME add other L3 proto IDs */ - return gen_mpls_linktype(cstate, Q_IP); - - case ETHERTYPE_IPV6: - case PPP_IPV6: - /* FIXME add other L3 proto IDs */ - return gen_mpls_linktype(cstate, Q_IPV6); - - default: - bpf_error(cstate, "unsupported protocol over mpls"); - /*NOTREACHED*/ - } - } + if (cstate->label_stack_depth > 0) + return gen_mpls_linktype(cstate, ll_proto); switch (cstate->linktype) { @@ -3169,21 +3209,22 @@ gen_linktype(compiler_state_t *cstate, int proto) else b0 = NULL; - b1 = gen_ether_linktype(cstate, proto); + b1 = gen_ether_linktype(cstate, ll_proto); if (b0 != NULL) gen_and(b0, b1); return b1; /*NOTREACHED*/ case DLT_C_HDLC: - switch (proto) { + case DLT_HDLC: + switch (ll_proto) { case LLCSAP_ISONS: - proto = (proto << 8 | LLCSAP_ISONS); + ll_proto = (ll_proto << 8 | LLCSAP_ISONS); /* fall through */ default: - return gen_cmp(cstate, OR_LINKTYPE, 0, BPF_H, (bpf_int32)proto); + return gen_cmp(cstate, OR_LINKTYPE, 0, BPF_H, ll_proto); /*NOTREACHED*/ } @@ -3200,7 +3241,7 @@ gen_linktype(compiler_state_t *cstate, int proto) /* * Now check for the specified link-layer type. */ - b1 = gen_llc_linktype(cstate, proto); + b1 = gen_llc_linktype(cstate, ll_proto); gen_and(b0, b1); return b1; /*NOTREACHED*/ @@ -3209,20 +3250,20 @@ gen_linktype(compiler_state_t *cstate, int proto) /* * XXX - check for LLC frames. */ - return gen_llc_linktype(cstate, proto); + return gen_llc_linktype(cstate, ll_proto); /*NOTREACHED*/ case DLT_IEEE802: /* * XXX - check for LLC PDUs, as per IEEE 802.5. */ - return gen_llc_linktype(cstate, proto); + return gen_llc_linktype(cstate, ll_proto); /*NOTREACHED*/ case DLT_ATM_RFC1483: case DLT_ATM_CLIP: case DLT_IP_OVER_FC: - return gen_llc_linktype(cstate, proto); + return gen_llc_linktype(cstate, ll_proto); /*NOTREACHED*/ case DLT_SUNATM: @@ -3234,13 +3275,13 @@ gen_linktype(compiler_state_t *cstate, int proto) * Check for LLC encapsulation and then check the protocol. */ b0 = gen_atmfield_code_internal(cstate, A_PROTOTYPE, PT_LLC, BPF_JEQ, 0); - b1 = gen_llc_linktype(cstate, proto); + b1 = gen_llc_linktype(cstate, ll_proto); gen_and(b0, b1); return b1; /*NOTREACHED*/ case DLT_LINUX_SLL: - return gen_linux_sll_linktype(cstate, proto); + return gen_linux_sll_linktype(cstate, ll_proto); /*NOTREACHED*/ case DLT_SLIP: @@ -3253,7 +3294,7 @@ gen_linktype(compiler_state_t *cstate, int proto) * XXX - for IPv4, check for a version number of 4, and, * for IPv6, check for a version number of 6? */ - switch (proto) { + switch (ll_proto) { case ETHERTYPE_IP: /* Check for a version number of 4. */ @@ -3272,7 +3313,7 @@ gen_linktype(compiler_state_t *cstate, int proto) /* * Raw IPv4, so no type field. */ - if (proto == ETHERTYPE_IP) + if (ll_proto == ETHERTYPE_IP) return gen_true(cstate); /* always true */ /* Checking for something other than IPv4; always false */ @@ -3283,7 +3324,7 @@ gen_linktype(compiler_state_t *cstate, int proto) /* * Raw IPv6, so no type field. */ - if (proto == ETHERTYPE_IPV6) + if (ll_proto == ETHERTYPE_IPV6) return gen_true(cstate); /* always true */ /* Checking for something other than IPv6; always false */ @@ -3298,8 +3339,8 @@ gen_linktype(compiler_state_t *cstate, int proto) * We use Ethernet protocol types inside libpcap; * map them to the corresponding PPP protocol types. */ - proto = ethertype_to_ppptype(proto); - return gen_cmp(cstate, OR_LINKTYPE, 0, BPF_H, (bpf_int32)proto); + return gen_cmp(cstate, OR_LINKTYPE, 0, BPF_H, + ethertype_to_ppptype(ll_proto)); /*NOTREACHED*/ case DLT_PPP_BSDOS: @@ -3307,7 +3348,7 @@ gen_linktype(compiler_state_t *cstate, int proto) * We use Ethernet protocol types inside libpcap; * map them to the corresponding PPP protocol types. */ - switch (proto) { + switch (ll_proto) { case ETHERTYPE_IP: /* @@ -3322,16 +3363,15 @@ gen_linktype(compiler_state_t *cstate, int proto) return b0; default: - proto = ethertype_to_ppptype(proto); return gen_cmp(cstate, OR_LINKTYPE, 0, BPF_H, - (bpf_int32)proto); + ethertype_to_ppptype(ll_proto)); } /*NOTREACHED*/ case DLT_NULL: case DLT_LOOP: case DLT_ENC: - switch (proto) { + switch (ll_proto) { case ETHERTYPE_IP: return (gen_loopback_linktype(cstate, AF_INET)); @@ -3406,22 +3446,20 @@ gen_linktype(compiler_state_t *cstate, int proto) return gen_false(cstate); } -#ifdef HAVE_NET_PFVAR_H case DLT_PFLOG: /* * af field is host byte order in contrast to the rest of * the packet. */ - if (proto == ETHERTYPE_IP) + if (ll_proto == ETHERTYPE_IP) return (gen_cmp(cstate, OR_LINKHDR, offsetof(struct pfloghdr, af), - BPF_B, (bpf_int32)AF_INET)); - else if (proto == ETHERTYPE_IPV6) + BPF_B, AF_INET)); + else if (ll_proto == ETHERTYPE_IPV6) return (gen_cmp(cstate, OR_LINKHDR, offsetof(struct pfloghdr, af), - BPF_B, (bpf_int32)AF_INET6)); + BPF_B, AF_INET6)); else return gen_false(cstate); /*NOTREACHED*/ -#endif /* HAVE_NET_PFVAR_H */ case DLT_ARCNET: case DLT_ARCNET_LINUX: @@ -3429,43 +3467,43 @@ gen_linktype(compiler_state_t *cstate, int proto) * XXX should we check for first fragment if the protocol * uses PHDS? */ - switch (proto) { + switch (ll_proto) { default: return gen_false(cstate); case ETHERTYPE_IPV6: return (gen_cmp(cstate, OR_LINKTYPE, 0, BPF_B, - (bpf_int32)ARCTYPE_INET6)); + ARCTYPE_INET6)); case ETHERTYPE_IP: b0 = gen_cmp(cstate, OR_LINKTYPE, 0, BPF_B, - (bpf_int32)ARCTYPE_IP); + ARCTYPE_IP); b1 = gen_cmp(cstate, OR_LINKTYPE, 0, BPF_B, - (bpf_int32)ARCTYPE_IP_OLD); + ARCTYPE_IP_OLD); gen_or(b0, b1); return (b1); case ETHERTYPE_ARP: b0 = gen_cmp(cstate, OR_LINKTYPE, 0, BPF_B, - (bpf_int32)ARCTYPE_ARP); + ARCTYPE_ARP); b1 = gen_cmp(cstate, OR_LINKTYPE, 0, BPF_B, - (bpf_int32)ARCTYPE_ARP_OLD); + ARCTYPE_ARP_OLD); gen_or(b0, b1); return (b1); case ETHERTYPE_REVARP: return (gen_cmp(cstate, OR_LINKTYPE, 0, BPF_B, - (bpf_int32)ARCTYPE_REVARP)); + ARCTYPE_REVARP)); case ETHERTYPE_ATALK: return (gen_cmp(cstate, OR_LINKTYPE, 0, BPF_B, - (bpf_int32)ARCTYPE_ATALK)); + ARCTYPE_ATALK)); } /*NOTREACHED*/ case DLT_LTALK: - switch (proto) { + switch (ll_proto) { case ETHERTYPE_ATALK: return gen_true(cstate); default: @@ -3478,7 +3516,7 @@ gen_linktype(compiler_state_t *cstate, int proto) * XXX - assumes a 2-byte Frame Relay header with * DLCI and flags. What if the address is longer? */ - switch (proto) { + switch (ll_proto) { case ETHERTYPE_IP: /* @@ -3555,7 +3593,7 @@ gen_linktype(compiler_state_t *cstate, int proto) return gen_mcmp(cstate, OR_LINKHDR, 0, BPF_W, 0x55FF0000, 0xffff0000); case DLT_IPNET: - return gen_ipnet_linktype(cstate, proto); + return gen_ipnet_linktype(cstate, ll_proto); case DLT_LINUX_IRDA: bpf_error(cstate, "IrDA link-layer type filtering not implemented"); @@ -3594,6 +3632,7 @@ gen_linktype(compiler_state_t *cstate, int proto) case DLT_IEEE802_15_4_LINUX: case DLT_IEEE802_15_4_NONASK_PHY: case DLT_IEEE802_15_4_NOFCS: + case DLT_IEEE802_15_4_TAP: bpf_error(cstate, "IEEE 802.15.4 link-layer type filtering not implemented"); case DLT_IEEE802_16_MAC_CPS_RADIO: @@ -3632,7 +3671,7 @@ gen_linktype(compiler_state_t *cstate, int proto) * it's not, it needs to be handled specially * above.) */ - return gen_cmp(cstate, OR_LINKTYPE, 0, BPF_H, (bpf_int32)proto); + return gen_cmp(cstate, OR_LINKTYPE, 0, BPF_H, ll_proto); /*NOTREACHED */ } else { /* @@ -3691,7 +3730,7 @@ gen_llc_internal(compiler_state_t *cstate) * Now check for the purported DSAP and SSAP not being * 0xFF, to rule out NetWare-over-802.3. */ - b1 = gen_cmp(cstate, OR_LLC, 0, BPF_H, (bpf_int32)0xFFFF); + b1 = gen_cmp(cstate, OR_LLC, 0, BPF_H, 0xFFFF); gen_not(b1); gen_and(b0, b1); return b1; @@ -3903,12 +3942,12 @@ gen_llc_u_subtype(compiler_state_t *cstate, bpf_u_int32 subtype) * protocol ID in a SNAP header. */ static struct block * -gen_llc_linktype(compiler_state_t *cstate, int proto) +gen_llc_linktype(compiler_state_t *cstate, bpf_u_int32 ll_proto) { /* * XXX - handle token-ring variable-length header. */ - switch (proto) { + switch (ll_proto) { case LLCSAP_IP: case LLCSAP_ISONS: @@ -3919,15 +3958,14 @@ gen_llc_linktype(compiler_state_t *cstate, int proto) * DSAP, as we do for other SAP values? */ return gen_cmp(cstate, OR_LLC, 0, BPF_H, (bpf_u_int32) - ((proto << 8) | proto)); + ((ll_proto << 8) | ll_proto)); case LLCSAP_IPX: /* * XXX - are there ever SNAP frames for IPX on * non-Ethernet 802.x networks? */ - return gen_cmp(cstate, OR_LLC, 0, BPF_B, - (bpf_int32)LLCSAP_IPX); + return gen_cmp(cstate, OR_LLC, 0, BPF_B, LLCSAP_IPX); case ETHERTYPE_ATALK: /* @@ -3946,12 +3984,12 @@ gen_llc_linktype(compiler_state_t *cstate, int proto) * XXX - we don't have to check for IPX 802.3 * here, but should we check for the IPX Ethertype? */ - if (proto <= ETHERMTU) { + if (ll_proto <= ETHERMTU) { /* * This is an LLC SAP value, so check * the DSAP. */ - return gen_cmp(cstate, OR_LLC, 0, BPF_B, (bpf_int32)proto); + return gen_cmp(cstate, OR_LLC, 0, BPF_B, ll_proto); } else { /* * This is an Ethernet type; we assume that it's @@ -3966,20 +4004,20 @@ gen_llc_linktype(compiler_state_t *cstate, int proto) * organization code of 0x000000 (encapsulated * Ethernet), we'd do * - * return gen_snap(cstate, 0x000000, proto); + * return gen_snap(cstate, 0x000000, ll_proto); * * here; for now, we don't, as per the above. * I don't know whether it's worth the extra CPU * time to do the right check or not. */ - return gen_cmp(cstate, OR_LLC, 6, BPF_H, (bpf_int32)proto); + return gen_cmp(cstate, OR_LLC, 6, BPF_H, ll_proto); } } } static struct block * gen_hostop(compiler_state_t *cstate, bpf_u_int32 addr, bpf_u_int32 mask, - int dir, int proto, u_int src_off, u_int dst_off) + int dir, bpf_u_int32 ll_proto, u_int src_off, u_int dst_off) { struct block *b0, *b1; u_int offset; @@ -3995,15 +4033,15 @@ gen_hostop(compiler_state_t *cstate, bpf_u_int32 addr, bpf_u_int32 mask, break; case Q_AND: - b0 = gen_hostop(cstate, addr, mask, Q_SRC, proto, src_off, dst_off); - b1 = gen_hostop(cstate, addr, mask, Q_DST, proto, src_off, dst_off); + b0 = gen_hostop(cstate, addr, mask, Q_SRC, ll_proto, src_off, dst_off); + b1 = gen_hostop(cstate, addr, mask, Q_DST, ll_proto, src_off, dst_off); gen_and(b0, b1); return b1; case Q_DEFAULT: case Q_OR: - b0 = gen_hostop(cstate, addr, mask, Q_SRC, proto, src_off, dst_off); - b1 = gen_hostop(cstate, addr, mask, Q_DST, proto, src_off, dst_off); + b0 = gen_hostop(cstate, addr, mask, Q_SRC, ll_proto, src_off, dst_off); + b1 = gen_hostop(cstate, addr, mask, Q_DST, ll_proto, src_off, dst_off); gen_or(b0, b1); return b1; @@ -4035,8 +4073,8 @@ gen_hostop(compiler_state_t *cstate, bpf_u_int32 addr, bpf_u_int32 mask, abort(); /*NOTREACHED*/ } - b0 = gen_linktype(cstate, proto); - b1 = gen_mcmp(cstate, OR_LINKPL, offset, BPF_W, (bpf_int32)addr, mask); + b0 = gen_linktype(cstate, ll_proto); + b1 = gen_mcmp(cstate, OR_LINKPL, offset, BPF_W, addr, mask); gen_and(b0, b1); return b1; } @@ -4044,7 +4082,8 @@ gen_hostop(compiler_state_t *cstate, bpf_u_int32 addr, bpf_u_int32 mask, #ifdef INET6 static struct block * gen_hostop6(compiler_state_t *cstate, struct in6_addr *addr, - struct in6_addr *mask, int dir, int proto, u_int src_off, u_int dst_off) + struct in6_addr *mask, int dir, bpf_u_int32 ll_proto, u_int src_off, + u_int dst_off) { struct block *b0, *b1; u_int offset; @@ -4061,15 +4100,15 @@ gen_hostop6(compiler_state_t *cstate, struct in6_addr *addr, break; case Q_AND: - b0 = gen_hostop6(cstate, addr, mask, Q_SRC, proto, src_off, dst_off); - b1 = gen_hostop6(cstate, addr, mask, Q_DST, proto, src_off, dst_off); + b0 = gen_hostop6(cstate, addr, mask, Q_SRC, ll_proto, src_off, dst_off); + b1 = gen_hostop6(cstate, addr, mask, Q_DST, ll_proto, src_off, dst_off); gen_and(b0, b1); return b1; case Q_DEFAULT: case Q_OR: - b0 = gen_hostop6(cstate, addr, mask, Q_SRC, proto, src_off, dst_off); - b1 = gen_hostop6(cstate, addr, mask, Q_DST, proto, src_off, dst_off); + b0 = gen_hostop6(cstate, addr, mask, Q_SRC, ll_proto, src_off, dst_off); + b1 = gen_hostop6(cstate, addr, mask, Q_DST, ll_proto, src_off, dst_off); gen_or(b0, b1); return b1; @@ -4111,7 +4150,7 @@ gen_hostop6(compiler_state_t *cstate, struct in6_addr *addr, gen_and(b0, b1); b0 = gen_mcmp(cstate, OR_LINKPL, offset + 0, BPF_W, ntohl(a[0]), ntohl(m[0])); gen_and(b0, b1); - b0 = gen_linktype(cstate, proto); + b0 = gen_linktype(cstate, ll_proto); gen_and(b0, b1); return b1; } @@ -4846,24 +4885,29 @@ gen_dnhostop(compiler_state_t *cstate, bpf_u_int32 addr, int dir) b0 = gen_linktype(cstate, ETHERTYPE_DN); /* Check for pad = 1, long header case */ tmp = gen_mcmp(cstate, OR_LINKPL, 2, BPF_H, - (bpf_int32)ntohs(0x0681), (bpf_int32)ntohs(0x07FF)); + (bpf_u_int32)ntohs(0x0681), (bpf_u_int32)ntohs(0x07FF)); b1 = gen_cmp(cstate, OR_LINKPL, 2 + 1 + offset_lh, - BPF_H, (bpf_int32)ntohs((u_short)addr)); + BPF_H, (bpf_u_int32)ntohs((u_short)addr)); gen_and(tmp, b1); /* Check for pad = 0, long header case */ - tmp = gen_mcmp(cstate, OR_LINKPL, 2, BPF_B, (bpf_int32)0x06, (bpf_int32)0x7); - b2 = gen_cmp(cstate, OR_LINKPL, 2 + offset_lh, BPF_H, (bpf_int32)ntohs((u_short)addr)); + tmp = gen_mcmp(cstate, OR_LINKPL, 2, BPF_B, (bpf_u_int32)0x06, + (bpf_u_int32)0x7); + b2 = gen_cmp(cstate, OR_LINKPL, 2 + offset_lh, BPF_H, + (bpf_u_int32)ntohs((u_short)addr)); gen_and(tmp, b2); gen_or(b2, b1); /* Check for pad = 1, short header case */ tmp = gen_mcmp(cstate, OR_LINKPL, 2, BPF_H, - (bpf_int32)ntohs(0x0281), (bpf_int32)ntohs(0x07FF)); - b2 = gen_cmp(cstate, OR_LINKPL, 2 + 1 + offset_sh, BPF_H, (bpf_int32)ntohs((u_short)addr)); + (bpf_u_int32)ntohs(0x0281), (bpf_u_int32)ntohs(0x07FF)); + b2 = gen_cmp(cstate, OR_LINKPL, 2 + 1 + offset_sh, BPF_H, + (bpf_u_int32)ntohs((u_short)addr)); gen_and(tmp, b2); gen_or(b2, b1); /* Check for pad = 0, short header case */ - tmp = gen_mcmp(cstate, OR_LINKPL, 2, BPF_B, (bpf_int32)0x02, (bpf_int32)0x7); - b2 = gen_cmp(cstate, OR_LINKPL, 2 + offset_sh, BPF_H, (bpf_int32)ntohs((u_short)addr)); + tmp = gen_mcmp(cstate, OR_LINKPL, 2, BPF_B, (bpf_u_int32)0x02, + (bpf_u_int32)0x7); + b2 = gen_cmp(cstate, OR_LINKPL, 2 + offset_sh, BPF_H, + (bpf_u_int32)ntohs((u_short)addr)); gen_and(tmp, b2); gen_or(b2, b1); @@ -4878,13 +4922,13 @@ gen_dnhostop(compiler_state_t *cstate, bpf_u_int32 addr, int dir) * field in the IP header. */ static struct block * -gen_mpls_linktype(compiler_state_t *cstate, int proto) +gen_mpls_linktype(compiler_state_t *cstate, bpf_u_int32 ll_proto) { struct block *b0, *b1; - switch (proto) { + switch (ll_proto) { - case Q_IP: + case ETHERTYPE_IP: /* match the bottom-of-stack bit */ b0 = gen_mcmp(cstate, OR_LINKPL, (u_int)-2, BPF_B, 0x01, 0x01); /* match the IPv4 version number */ @@ -4892,7 +4936,7 @@ gen_mpls_linktype(compiler_state_t *cstate, int proto) gen_and(b0, b1); return b1; - case Q_IPV6: + case ETHERTYPE_IPV6: /* match the bottom-of-stack bit */ b0 = gen_mcmp(cstate, OR_LINKPL, (u_int)-2, BPF_B, 0x01, 0x01); /* match the IPv4 version number */ @@ -4900,8 +4944,10 @@ gen_mpls_linktype(compiler_state_t *cstate, int proto) gen_and(b0, b1); return b1; - default: - abort(); + default: + /* FIXME add other L3 proto IDs */ + bpf_error(cstate, "unsupported protocol over mpls"); + /*NOTREACHED*/ } } @@ -5313,21 +5359,15 @@ gen_proto_abbrev_internal(compiler_state_t *cstate, int proto) switch (proto) { case Q_SCTP: - b1 = gen_proto(cstate, IPPROTO_SCTP, Q_IP, Q_DEFAULT); - b0 = gen_proto(cstate, IPPROTO_SCTP, Q_IPV6, Q_DEFAULT); - gen_or(b0, b1); + b1 = gen_proto(cstate, IPPROTO_SCTP, Q_DEFAULT, Q_DEFAULT); break; case Q_TCP: - b1 = gen_proto(cstate, IPPROTO_TCP, Q_IP, Q_DEFAULT); - b0 = gen_proto(cstate, IPPROTO_TCP, Q_IPV6, Q_DEFAULT); - gen_or(b0, b1); + b1 = gen_proto(cstate, IPPROTO_TCP, Q_DEFAULT, Q_DEFAULT); break; case Q_UDP: - b1 = gen_proto(cstate, IPPROTO_UDP, Q_IP, Q_DEFAULT); - b0 = gen_proto(cstate, IPPROTO_UDP, Q_IPV6, Q_DEFAULT); - gen_or(b0, b1); + b1 = gen_proto(cstate, IPPROTO_UDP, Q_DEFAULT, Q_DEFAULT); break; case Q_ICMP: @@ -5354,9 +5394,7 @@ gen_proto_abbrev_internal(compiler_state_t *cstate, int proto) #endif case Q_PIM: - b1 = gen_proto(cstate, IPPROTO_PIM, Q_IP, Q_DEFAULT); - b0 = gen_proto(cstate, IPPROTO_PIM, Q_IPV6, Q_DEFAULT); - gen_or(b0, b1); + b1 = gen_proto(cstate, IPPROTO_PIM, Q_DEFAULT, Q_DEFAULT); break; #ifndef IPPROTO_VRRP @@ -5433,18 +5471,14 @@ gen_proto_abbrev_internal(compiler_state_t *cstate, int proto) #define IPPROTO_AH 51 #endif case Q_AH: - b1 = gen_proto(cstate, IPPROTO_AH, Q_IP, Q_DEFAULT); - b0 = gen_proto(cstate, IPPROTO_AH, Q_IPV6, Q_DEFAULT); - gen_or(b0, b1); + b1 = gen_proto(cstate, IPPROTO_AH, Q_DEFAULT, Q_DEFAULT); break; #ifndef IPPROTO_ESP #define IPPROTO_ESP 50 #endif case Q_ESP: - b1 = gen_proto(cstate, IPPROTO_ESP, Q_IP, Q_DEFAULT); - b0 = gen_proto(cstate, IPPROTO_ESP, Q_IPV6, Q_DEFAULT); - gen_or(b0, b1); + b1 = gen_proto(cstate, IPPROTO_ESP, Q_DEFAULT, Q_DEFAULT); break; case Q_ISO: @@ -5583,46 +5617,46 @@ gen_ipfrag(compiler_state_t *cstate) * headers). */ static struct block * -gen_portatom(compiler_state_t *cstate, int off, bpf_int32 v) +gen_portatom(compiler_state_t *cstate, int off, bpf_u_int32 v) { return gen_cmp(cstate, OR_TRAN_IPV4, off, BPF_H, v); } static struct block * -gen_portatom6(compiler_state_t *cstate, int off, bpf_int32 v) +gen_portatom6(compiler_state_t *cstate, int off, bpf_u_int32 v) { return gen_cmp(cstate, OR_TRAN_IPV6, off, BPF_H, v); } -struct block * -gen_portop(compiler_state_t *cstate, int port, int proto, int dir) +static struct block * +gen_portop(compiler_state_t *cstate, u_int port, u_int proto, int dir) { struct block *b0, *b1, *tmp; /* ip proto 'proto' and not a fragment other than the first fragment */ - tmp = gen_cmp(cstate, OR_LINKPL, 9, BPF_B, (bpf_int32)proto); + tmp = gen_cmp(cstate, OR_LINKPL, 9, BPF_B, proto); b0 = gen_ipfrag(cstate); gen_and(tmp, b0); switch (dir) { case Q_SRC: - b1 = gen_portatom(cstate, 0, (bpf_int32)port); + b1 = gen_portatom(cstate, 0, port); break; case Q_DST: - b1 = gen_portatom(cstate, 2, (bpf_int32)port); + b1 = gen_portatom(cstate, 2, port); break; case Q_AND: - tmp = gen_portatom(cstate, 0, (bpf_int32)port); - b1 = gen_portatom(cstate, 2, (bpf_int32)port); + tmp = gen_portatom(cstate, 0, port); + b1 = gen_portatom(cstate, 2, port); gen_and(tmp, b1); break; case Q_DEFAULT: case Q_OR: - tmp = gen_portatom(cstate, 0, (bpf_int32)port); - b1 = gen_portatom(cstate, 2, (bpf_int32)port); + tmp = gen_portatom(cstate, 0, port); + b1 = gen_portatom(cstate, 2, port); gen_or(tmp, b1); break; @@ -5660,7 +5694,7 @@ gen_portop(compiler_state_t *cstate, int port, int proto, int dir) } static struct block * -gen_port(compiler_state_t *cstate, int port, int ip_proto, int dir) +gen_port(compiler_state_t *cstate, u_int port, int ip_proto, int dir) { struct block *b0, *b1, *tmp; @@ -5687,7 +5721,7 @@ gen_port(compiler_state_t *cstate, int port, int ip_proto, int dir) case IPPROTO_UDP: case IPPROTO_TCP: case IPPROTO_SCTP: - b1 = gen_portop(cstate, port, ip_proto, dir); + b1 = gen_portop(cstate, port, (u_int)ip_proto, dir); break; case PROTO_UNDEF: @@ -5706,33 +5740,33 @@ gen_port(compiler_state_t *cstate, int port, int ip_proto, int dir) } struct block * -gen_portop6(compiler_state_t *cstate, int port, int proto, int dir) +gen_portop6(compiler_state_t *cstate, u_int port, u_int proto, int dir) { struct block *b0, *b1, *tmp; /* ip6 proto 'proto' */ /* XXX - catch the first fragment of a fragmented packet? */ - b0 = gen_cmp(cstate, OR_LINKPL, 6, BPF_B, (bpf_int32)proto); + b0 = gen_cmp(cstate, OR_LINKPL, 6, BPF_B, proto); switch (dir) { case Q_SRC: - b1 = gen_portatom6(cstate, 0, (bpf_int32)port); + b1 = gen_portatom6(cstate, 0, port); break; case Q_DST: - b1 = gen_portatom6(cstate, 2, (bpf_int32)port); + b1 = gen_portatom6(cstate, 2, port); break; case Q_AND: - tmp = gen_portatom6(cstate, 0, (bpf_int32)port); - b1 = gen_portatom6(cstate, 2, (bpf_int32)port); + tmp = gen_portatom6(cstate, 0, port); + b1 = gen_portatom6(cstate, 2, port); gen_and(tmp, b1); break; case Q_DEFAULT: case Q_OR: - tmp = gen_portatom6(cstate, 0, (bpf_int32)port); - b1 = gen_portatom6(cstate, 2, (bpf_int32)port); + tmp = gen_portatom6(cstate, 0, port); + b1 = gen_portatom6(cstate, 2, port); gen_or(tmp, b1); break; @@ -5745,7 +5779,7 @@ gen_portop6(compiler_state_t *cstate, int port, int proto, int dir) } static struct block * -gen_port6(compiler_state_t *cstate, int port, int ip_proto, int dir) +gen_port6(compiler_state_t *cstate, u_int port, int ip_proto, int dir) { struct block *b0, *b1, *tmp; @@ -5756,7 +5790,7 @@ gen_port6(compiler_state_t *cstate, int port, int ip_proto, int dir) case IPPROTO_UDP: case IPPROTO_TCP: case IPPROTO_SCTP: - b1 = gen_portop6(cstate, port, ip_proto, dir); + b1 = gen_portop6(cstate, port, (u_int)ip_proto, dir); break; case PROTO_UNDEF: @@ -5776,8 +5810,8 @@ gen_port6(compiler_state_t *cstate, int port, int ip_proto, int dir) /* gen_portrange code */ static struct block * -gen_portrangeatom(compiler_state_t *cstate, int off, bpf_int32 v1, - bpf_int32 v2) +gen_portrangeatom(compiler_state_t *cstate, u_int off, bpf_u_int32 v1, + bpf_u_int32 v2) { struct block *b1, *b2; @@ -5785,7 +5819,7 @@ gen_portrangeatom(compiler_state_t *cstate, int off, bpf_int32 v1, /* * Reverse the order of the ports, so v1 is the lower one. */ - bpf_int32 vtemp; + bpf_u_int32 vtemp; vtemp = v1; v1 = v2; @@ -5800,36 +5834,36 @@ gen_portrangeatom(compiler_state_t *cstate, int off, bpf_int32 v1, return b2; } -struct block * -gen_portrangeop(compiler_state_t *cstate, int port1, int port2, int proto, - int dir) +static struct block * +gen_portrangeop(compiler_state_t *cstate, u_int port1, u_int port2, + bpf_u_int32 proto, int dir) { struct block *b0, *b1, *tmp; /* ip proto 'proto' and not a fragment other than the first fragment */ - tmp = gen_cmp(cstate, OR_LINKPL, 9, BPF_B, (bpf_int32)proto); + tmp = gen_cmp(cstate, OR_LINKPL, 9, BPF_B, proto); b0 = gen_ipfrag(cstate); gen_and(tmp, b0); switch (dir) { case Q_SRC: - b1 = gen_portrangeatom(cstate, 0, (bpf_int32)port1, (bpf_int32)port2); + b1 = gen_portrangeatom(cstate, 0, port1, port2); break; case Q_DST: - b1 = gen_portrangeatom(cstate, 2, (bpf_int32)port1, (bpf_int32)port2); + b1 = gen_portrangeatom(cstate, 2, port1, port2); break; case Q_AND: - tmp = gen_portrangeatom(cstate, 0, (bpf_int32)port1, (bpf_int32)port2); - b1 = gen_portrangeatom(cstate, 2, (bpf_int32)port1, (bpf_int32)port2); + tmp = gen_portrangeatom(cstate, 0, port1, port2); + b1 = gen_portrangeatom(cstate, 2, port1, port2); gen_and(tmp, b1); break; case Q_DEFAULT: case Q_OR: - tmp = gen_portrangeatom(cstate, 0, (bpf_int32)port1, (bpf_int32)port2); - b1 = gen_portrangeatom(cstate, 2, (bpf_int32)port1, (bpf_int32)port2); + tmp = gen_portrangeatom(cstate, 0, port1, port2); + b1 = gen_portrangeatom(cstate, 2, port1, port2); gen_or(tmp, b1); break; @@ -5867,7 +5901,7 @@ gen_portrangeop(compiler_state_t *cstate, int port1, int port2, int proto, } static struct block * -gen_portrange(compiler_state_t *cstate, int port1, int port2, int ip_proto, +gen_portrange(compiler_state_t *cstate, u_int port1, u_int port2, int ip_proto, int dir) { struct block *b0, *b1, *tmp; @@ -5879,7 +5913,8 @@ gen_portrange(compiler_state_t *cstate, int port1, int port2, int ip_proto, case IPPROTO_UDP: case IPPROTO_TCP: case IPPROTO_SCTP: - b1 = gen_portrangeop(cstate, port1, port2, ip_proto, dir); + b1 = gen_portrangeop(cstate, port1, port2, (bpf_u_int32)ip_proto, + dir); break; case PROTO_UNDEF: @@ -5898,8 +5933,8 @@ gen_portrange(compiler_state_t *cstate, int port1, int port2, int ip_proto, } static struct block * -gen_portrangeatom6(compiler_state_t *cstate, int off, bpf_int32 v1, - bpf_int32 v2) +gen_portrangeatom6(compiler_state_t *cstate, u_int off, bpf_u_int32 v1, + bpf_u_int32 v2) { struct block *b1, *b2; @@ -5907,7 +5942,7 @@ gen_portrangeatom6(compiler_state_t *cstate, int off, bpf_int32 v1, /* * Reverse the order of the ports, so v1 is the lower one. */ - bpf_int32 vtemp; + bpf_u_int32 vtemp; vtemp = v1; v1 = v2; @@ -5922,35 +5957,35 @@ gen_portrangeatom6(compiler_state_t *cstate, int off, bpf_int32 v1, return b2; } -struct block * -gen_portrangeop6(compiler_state_t *cstate, int port1, int port2, int proto, - int dir) +static struct block * +gen_portrangeop6(compiler_state_t *cstate, u_int port1, u_int port2, + bpf_u_int32 proto, int dir) { struct block *b0, *b1, *tmp; /* ip6 proto 'proto' */ /* XXX - catch the first fragment of a fragmented packet? */ - b0 = gen_cmp(cstate, OR_LINKPL, 6, BPF_B, (bpf_int32)proto); + b0 = gen_cmp(cstate, OR_LINKPL, 6, BPF_B, proto); switch (dir) { case Q_SRC: - b1 = gen_portrangeatom6(cstate, 0, (bpf_int32)port1, (bpf_int32)port2); + b1 = gen_portrangeatom6(cstate, 0, port1, port2); break; case Q_DST: - b1 = gen_portrangeatom6(cstate, 2, (bpf_int32)port1, (bpf_int32)port2); + b1 = gen_portrangeatom6(cstate, 2, port1, port2); break; case Q_AND: - tmp = gen_portrangeatom6(cstate, 0, (bpf_int32)port1, (bpf_int32)port2); - b1 = gen_portrangeatom6(cstate, 2, (bpf_int32)port1, (bpf_int32)port2); + tmp = gen_portrangeatom6(cstate, 0, port1, port2); + b1 = gen_portrangeatom6(cstate, 2, port1, port2); gen_and(tmp, b1); break; case Q_DEFAULT: case Q_OR: - tmp = gen_portrangeatom6(cstate, 0, (bpf_int32)port1, (bpf_int32)port2); - b1 = gen_portrangeatom6(cstate, 2, (bpf_int32)port1, (bpf_int32)port2); + tmp = gen_portrangeatom6(cstate, 0, port1, port2); + b1 = gen_portrangeatom6(cstate, 2, port1, port2); gen_or(tmp, b1); break; @@ -5963,7 +5998,7 @@ gen_portrangeop6(compiler_state_t *cstate, int port1, int port2, int proto, } static struct block * -gen_portrange6(compiler_state_t *cstate, int port1, int port2, int ip_proto, +gen_portrange6(compiler_state_t *cstate, u_int port1, u_int port2, int ip_proto, int dir) { struct block *b0, *b1, *tmp; @@ -5975,7 +6010,8 @@ gen_portrange6(compiler_state_t *cstate, int port1, int port2, int ip_proto, case IPPROTO_UDP: case IPPROTO_TCP: case IPPROTO_SCTP: - b1 = gen_portrangeop6(cstate, port1, port2, ip_proto, dir); + b1 = gen_portrangeop6(cstate, port1, port2, (bpf_u_int32)ip_proto, + dir); break; case PROTO_UNDEF: @@ -6036,20 +6072,10 @@ lookup_proto(compiler_state_t *cstate, const char *name, int proto) return v; } -#if 0 -struct stmt * -gen_joinsp(struct stmt **s, int n) -{ - return NULL; -} -#endif - +#if !defined(NO_PROTOCHAIN) static struct block * -gen_protochain(compiler_state_t *cstate, int v, int proto, int dir) +gen_protochain(compiler_state_t *cstate, bpf_u_int32 v, int proto) { -#ifdef NO_PROTOCHAIN - return gen_proto(cstate, v, proto, dir); -#else struct block *b0, *b; struct slist *s[100]; int fix2, fix3, fix4, fix5; @@ -6065,8 +6091,8 @@ gen_protochain(compiler_state_t *cstate, int v, int proto, int dir) case Q_IPV6: break; case Q_DEFAULT: - b0 = gen_protochain(cstate, v, Q_IP, dir); - b = gen_protochain(cstate, v, Q_IPV6, dir); + b0 = gen_protochain(cstate, v, Q_IP); + b = gen_protochain(cstate, v, Q_IPV6); gen_or(b0, b); return b; default: @@ -6088,7 +6114,18 @@ gen_protochain(compiler_state_t *cstate, int v, int proto, int dir) if (cstate->off_linkpl.is_variable) bpf_error(cstate, "'protochain' not supported with variable length headers"); - cstate->no_optimize = 1; /* this code is not compatible with optimizer yet */ + /* + * To quote a comment in optimize.c: + * + * "These data structures are used in a Cocke and Shwarz style + * value numbering scheme. Since the flowgraph is acyclic, + * exit values can be propagated from a node's predecessors + * provided it is uniquely defined." + * + * "Acyclic" means "no backward branches", which means "no + * loops", so we have to turn the optimizer off. + */ + cstate->no_optimize = 1; /* * s[0] is a dummy entry to protect other BPF insn from damage @@ -6332,8 +6369,8 @@ gen_protochain(compiler_state_t *cstate, int v, int proto, int dir) gen_and(b0, b); return b; -#endif } +#endif /* !defined(NO_PROTOCHAIN) */ static struct block * gen_check_802_11_data_frame(compiler_state_t *cstate) @@ -6371,12 +6408,10 @@ gen_check_802_11_data_frame(compiler_state_t *cstate) * against Q_IP and Q_IPV6. */ static struct block * -gen_proto(compiler_state_t *cstate, int v, int proto, int dir) +gen_proto(compiler_state_t *cstate, bpf_u_int32 v, int proto, int dir) { struct block *b0, *b1; -#ifndef CHASE_CHAIN struct block *b2; -#endif if (dir != Q_DEFAULT) bpf_error(cstate, "direction applied to 'proto'"); @@ -6408,11 +6443,7 @@ gen_proto(compiler_state_t *cstate, int v, int proto, int dir) * So we always check for ETHERTYPE_IP. */ b0 = gen_linktype(cstate, ETHERTYPE_IP); -#ifndef CHASE_CHAIN - b1 = gen_cmp(cstate, OR_LINKPL, 9, BPF_B, (bpf_int32)v); -#else - b1 = gen_protochain(cstate, v, Q_IP); -#endif + b1 = gen_cmp(cstate, OR_LINKPL, 9, BPF_B, v); gen_and(b0, b1); return b1; @@ -6474,19 +6505,15 @@ gen_proto(compiler_state_t *cstate, int v, int proto, int dir) case Q_IPV6: b0 = gen_linktype(cstate, ETHERTYPE_IPV6); -#ifndef CHASE_CHAIN /* * Also check for a fragment header before the final * header. */ b2 = gen_cmp(cstate, OR_LINKPL, 6, BPF_B, IPPROTO_FRAGMENT); - b1 = gen_cmp(cstate, OR_LINKPL, 40, BPF_B, (bpf_int32)v); + b1 = gen_cmp(cstate, OR_LINKPL, 40, BPF_B, v); gen_and(b2, b1); - b2 = gen_cmp(cstate, OR_LINKPL, 6, BPF_B, (bpf_int32)v); + b2 = gen_cmp(cstate, OR_LINKPL, 6, BPF_B, v); gen_or(b2, b1); -#else - b1 = gen_protochain(cstate, v, Q_IPV6); -#endif gen_and(b0, b1); return b1; @@ -6499,7 +6526,7 @@ gen_proto(compiler_state_t *cstate, int v, int proto, int dir) /*NOTREACHED*/ case Q_ESP: - bpf_error(cstate, "'ah proto' is bogus"); + bpf_error(cstate, "'esp proto' is bogus"); /*NOTREACHED*/ case Q_PIM: @@ -6540,19 +6567,20 @@ gen_proto(compiler_state_t *cstate, int v, int proto, int dir) /*NOTREACHED*/ case DLT_C_HDLC: + case DLT_HDLC: /* * Cisco uses an Ethertype lookalike - for OSI, * it's 0xfefe. */ b0 = gen_linktype(cstate, LLCSAP_ISONS<<8 | LLCSAP_ISONS); /* OSI in C-HDLC is stuffed with a fudge byte */ - b1 = gen_cmp(cstate, OR_LINKPL_NOSNAP, 1, BPF_B, (long)v); + b1 = gen_cmp(cstate, OR_LINKPL_NOSNAP, 1, BPF_B, v); gen_and(b0, b1); return b1; default: b0 = gen_linktype(cstate, LLCSAP_ISONS); - b1 = gen_cmp(cstate, OR_LINKPL_NOSNAP, 0, BPF_B, (long)v); + b1 = gen_cmp(cstate, OR_LINKPL_NOSNAP, 0, BPF_B, v); gen_and(b0, b1); return b1; } @@ -6567,7 +6595,7 @@ gen_proto(compiler_state_t *cstate, int v, int proto, int dir) * 4 is the offset of the PDU type relative to the IS-IS * header. */ - b1 = gen_cmp(cstate, OR_LINKPL_NOSNAP, 4, BPF_B, (long)v); + b1 = gen_cmp(cstate, OR_LINKPL_NOSNAP, 4, BPF_B, v); gen_and(b0, b1); return b1; @@ -6925,12 +6953,14 @@ gen_scode(compiler_state_t *cstate, const char *name, struct qual q) else bpf_error(cstate, "unknown protocol: %s", name); +#if !defined(NO_PROTOCHAIN) case Q_PROTOCHAIN: real_proto = lookup_proto(cstate, name, proto); if (real_proto >= 0) - return gen_protochain(cstate, real_proto, proto, dir); + return gen_protochain(cstate, real_proto, proto); else bpf_error(cstate, "unknown protocol: %s", name); +#endif /* !defined(NO_PROTOCHAIN) */ case Q_UNDEF: syntax(cstate); @@ -6942,7 +6972,7 @@ gen_scode(compiler_state_t *cstate, const char *name, struct qual q) struct block * gen_mcode(compiler_state_t *cstate, const char *s1, const char *s2, - unsigned int masklen, struct qual q) + bpf_u_int32 masklen, struct qual q) { register int nlen, mlen; bpf_u_int32 n, m; @@ -7069,8 +7099,8 @@ gen_ncode(compiler_state_t *cstate, const char *s, bpf_u_int32 v, struct qual q) { struct block *b; - b = gen_port(cstate, (int)v, proto, dir); - gen_or(gen_port6(cstate, (int)v, proto, dir), b); + b = gen_port(cstate, v, proto, dir); + gen_or(gen_port6(cstate, v, proto, dir), b); return b; } @@ -7091,8 +7121,8 @@ gen_ncode(compiler_state_t *cstate, const char *s, bpf_u_int32 v, struct qual q) { struct block *b; - b = gen_portrange(cstate, (int)v, (int)v, proto, dir); - gen_or(gen_portrange6(cstate, (int)v, (int)v, proto, dir), b); + b = gen_portrange(cstate, v, v, proto, dir); + gen_or(gen_portrange6(cstate, v, v, proto, dir), b); return b; } @@ -7101,10 +7131,12 @@ gen_ncode(compiler_state_t *cstate, const char *s, bpf_u_int32 v, struct qual q) /*NOTREACHED*/ case Q_PROTO: - return gen_proto(cstate, (int)v, proto, dir); + return gen_proto(cstate, v, proto, dir); +#if !defined(NO_PROTOCHAIN) case Q_PROTOCHAIN: - return gen_protochain(cstate, (int)v, proto, dir); + return gen_protochain(cstate, v, proto); +#endif case Q_UNDEF: syntax(cstate); @@ -7120,7 +7152,7 @@ gen_ncode(compiler_state_t *cstate, const char *s, bpf_u_int32 v, struct qual q) #ifdef INET6 struct block * gen_mcode6(compiler_state_t *cstate, const char *s1, const char *s2, - unsigned int masklen, struct qual q) + bpf_u_int32 masklen, struct qual q) { struct addrinfo *res; struct in6_addr *addr; @@ -7146,10 +7178,10 @@ gen_mcode6(compiler_state_t *cstate, const char *s1, const char *s2, bpf_error(cstate, "%s resolved to multiple address", s1); addr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; - if (sizeof(mask) * 8 < masklen) - bpf_error(cstate, "mask length must be <= %u", (unsigned int)(sizeof(mask) * 8)); + if (masklen > sizeof(mask.s6_addr) * 8) + bpf_error(cstate, "mask length must be <= %u", (unsigned int)(sizeof(mask.s6_addr) * 8)); memset(&mask, 0, sizeof(mask)); - memset(&mask, 0xff, masklen / 8); + memset(&mask.s6_addr, 0xff, masklen / 8); if (masklen % 8) { mask.s6_addr[masklen / 8] = (0xff << (8 - masklen % 8)) & 0xff; @@ -7278,8 +7310,10 @@ xfer_to_a(compiler_state_t *cstate, struct arth *a) * for "index". */ static struct arth * -gen_load_internal(compiler_state_t *cstate, int proto, struct arth *inst, int size) +gen_load_internal(compiler_state_t *cstate, int proto, struct arth *inst, + bpf_u_int32 size) { + int size_code; struct slist *s, *tmp; struct block *b; int regno = alloc_reg(cstate); @@ -7289,17 +7323,18 @@ gen_load_internal(compiler_state_t *cstate, int proto, struct arth *inst, int si default: bpf_error(cstate, "data size must be 1, 2, or 4"); + /*NOTREACHED*/ case 1: - size = BPF_B; + size_code = BPF_B; break; case 2: - size = BPF_H; + size_code = BPF_H; break; case 4: - size = BPF_W; + size_code = BPF_W; break; } switch (proto) { @@ -7326,7 +7361,7 @@ gen_load_internal(compiler_state_t *cstate, int proto, struct arth *inst, int si /* * Load the item at that offset. */ - tmp = new_stmt(cstate, BPF_LD|BPF_IND|size); + tmp = new_stmt(cstate, BPF_LD|BPF_IND|size_code); sappend(s, tmp); sappend(inst->s, s); break; @@ -7368,7 +7403,7 @@ gen_load_internal(compiler_state_t *cstate, int proto, struct arth *inst, int si * variable-length; that header length is what we put * into the X register and then added to the index). */ - tmp = new_stmt(cstate, BPF_LD|BPF_IND|size); + tmp = new_stmt(cstate, BPF_LD|BPF_IND|size_code); tmp->s.k = cstate->off_linkhdr.constant_part; sappend(s, tmp); sappend(inst->s, s); @@ -7415,7 +7450,7 @@ gen_load_internal(compiler_state_t *cstate, int proto, struct arth *inst, int si * payload, and the constant part of the offset of the * start of the link-layer payload. */ - tmp = new_stmt(cstate, BPF_LD|BPF_IND|size); + tmp = new_stmt(cstate, BPF_LD|BPF_IND|size_code); tmp->s.k = cstate->off_linkpl.constant_part + cstate->off_nl; sappend(s, tmp); sappend(inst->s, s); @@ -7472,7 +7507,7 @@ gen_load_internal(compiler_state_t *cstate, int proto, struct arth *inst, int si sappend(s, xfer_to_a(cstate, inst)); sappend(s, new_stmt(cstate, BPF_ALU|BPF_ADD|BPF_X)); sappend(s, new_stmt(cstate, BPF_MISC|BPF_TAX)); - sappend(s, tmp = new_stmt(cstate, BPF_LD|BPF_IND|size)); + sappend(s, tmp = new_stmt(cstate, BPF_LD|BPF_IND|size_code)); tmp->s.k = cstate->off_linkpl.constant_part + cstate->off_nl; sappend(inst->s, s); @@ -7534,7 +7569,7 @@ gen_load_internal(compiler_state_t *cstate, int proto, struct arth *inst, int si * payload, and the constant part of the offset of the * start of the link-layer payload. */ - tmp = new_stmt(cstate, BPF_LD|BPF_IND|size); + tmp = new_stmt(cstate, BPF_LD|BPF_IND|size_code); tmp->s.k = cstate->off_linkpl.constant_part + cstate->off_nl + 40; sappend(s, tmp); @@ -7551,7 +7586,8 @@ gen_load_internal(compiler_state_t *cstate, int proto, struct arth *inst, int si } struct arth * -gen_load(compiler_state_t *cstate, int proto, struct arth *inst, int size) +gen_load(compiler_state_t *cstate, int proto, struct arth *inst, + bpf_u_int32 size) { /* * Catch errors reported by us and routines below us, and return NULL @@ -7647,7 +7683,7 @@ gen_loadlen(compiler_state_t *cstate) } static struct arth * -gen_loadi_internal(compiler_state_t *cstate, int val) +gen_loadi_internal(compiler_state_t *cstate, bpf_u_int32 val) { struct arth *a; struct slist *s; @@ -7668,7 +7704,7 @@ gen_loadi_internal(compiler_state_t *cstate, int val) } struct arth * -gen_loadi(compiler_state_t *cstate, int val) +gen_loadi(compiler_state_t *cstate, bpf_u_int32 val) { /* * Catch errors reported by us and routines below us, and return NULL @@ -7743,13 +7779,7 @@ gen_arth(compiler_state_t *cstate, int code, struct arth *a0_arg, if (a1->s->s.code == (BPF_LD|BPF_IMM) && a1->s->s.k == 0) bpf_error(cstate, "modulus by zero"); } else if (code == BPF_LSH || code == BPF_RSH) { - /* - * XXX - we need to make up our minds as to what integers - * are signed and what integers are unsigned in BPF programs - * and in our IR. - */ - if (a1->s->s.code == (BPF_LD|BPF_IMM) && - (a1->s->s.k < 0 || a1->s->s.k > 31)) + if (a1->s->s.code == (BPF_LD|BPF_IMM) && a1->s->s.k > 31) bpf_error(cstate, "shift by more than 31 bits"); } s0 = xfer_to_x(cstate, a1); @@ -7870,7 +7900,7 @@ gen_less(compiler_state_t *cstate, int n) * would generate code appropriate to the radio header in question. */ struct block * -gen_byteop(compiler_state_t *cstate, int op, int idx, int val) +gen_byteop(compiler_state_t *cstate, int op, int idx, bpf_u_int32 val) { struct block *b; struct slist *s; @@ -7887,14 +7917,14 @@ gen_byteop(compiler_state_t *cstate, int op, int idx, int val) abort(); case '=': - return gen_cmp(cstate, OR_LINKHDR, (u_int)idx, BPF_B, (bpf_int32)val); + return gen_cmp(cstate, OR_LINKHDR, (u_int)idx, BPF_B, val); case '<': - b = gen_cmp_lt(cstate, OR_LINKHDR, (u_int)idx, BPF_B, (bpf_int32)val); + b = gen_cmp_lt(cstate, OR_LINKHDR, (u_int)idx, BPF_B, val); return b; case '>': - b = gen_cmp_gt(cstate, OR_LINKHDR, (u_int)idx, BPF_B, (bpf_int32)val); + b = gen_cmp_gt(cstate, OR_LINKHDR, (u_int)idx, BPF_B, val); return b; case '|': @@ -7972,9 +8002,9 @@ gen_broadcast(compiler_state_t *cstate, int proto) bpf_error(cstate, "netmask not known, so 'ip broadcast' not supported"); b0 = gen_linktype(cstate, ETHERTYPE_IP); hostmask = ~cstate->netmask; - b1 = gen_mcmp(cstate, OR_LINKPL, 16, BPF_W, (bpf_int32)0, hostmask); + b1 = gen_mcmp(cstate, OR_LINKPL, 16, BPF_W, 0, hostmask); b2 = gen_mcmp(cstate, OR_LINKPL, 16, BPF_W, - (bpf_int32)(~0 & hostmask), hostmask); + ~0 & hostmask, hostmask); gen_or(b1, b2); gen_and(b0, b2); return b2; @@ -8171,13 +8201,13 @@ gen_multicast(compiler_state_t *cstate, int proto) case Q_IP: b0 = gen_linktype(cstate, ETHERTYPE_IP); - b1 = gen_cmp_ge(cstate, OR_LINKPL, 16, BPF_B, (bpf_int32)224); + b1 = gen_cmp_ge(cstate, OR_LINKPL, 16, BPF_B, 224); gen_and(b0, b1); return b1; case Q_IPV6: b0 = gen_linktype(cstate, ETHERTYPE_IPV6); - b1 = gen_cmp(cstate, OR_LINKPL, 24, BPF_B, (bpf_int32)255); + b1 = gen_cmp(cstate, OR_LINKPL, 24, BPF_B, 255); gen_and(b0, b1); return b1; } @@ -8185,6 +8215,53 @@ gen_multicast(compiler_state_t *cstate, int proto) /*NOTREACHED*/ } +struct block * +gen_ifindex(compiler_state_t *cstate, int ifindex) +{ + register struct block *b0; + + /* + * Catch errors reported by us and routines below us, and return NULL + * on an error. + */ + if (setjmp(cstate->top_ctx)) + return (NULL); + + /* + * Only some data link types support ifindex qualifiers. + */ + switch (cstate->linktype) { + case DLT_LINUX_SLL2: + /* match packets on this interface */ + b0 = gen_cmp(cstate, OR_LINKHDR, 4, BPF_W, ifindex); + break; + default: +#if defined(linux) + /* + * This is Linux; we require PF_PACKET support. + * If this is a *live* capture, we can look at + * special meta-data in the filter expression; + * if it's a savefile, we can't. + */ + if (cstate->bpf_pcap->rfile != NULL) { + /* We have a FILE *, so this is a savefile */ + bpf_error(cstate, "ifindex not supported on %s when reading savefiles", + pcap_datalink_val_to_description_or_dlt(cstate->linktype)); + b0 = NULL; + /*NOTREACHED*/ + } + /* match ifindex */ + b0 = gen_cmp(cstate, OR_LINKHDR, SKF_AD_OFF + SKF_AD_IFINDEX, BPF_W, + ifindex); +#else /* defined(linux) */ + bpf_error(cstate, "ifindex not supported on %s", + pcap_datalink_val_to_description_or_dlt(cstate->linktype)); + /*NOTREACHED*/ +#endif /* defined(linux) */ + } + return (b0); +} + /* * Filter on inbound (dir == 0) or outbound (dir == 1) traffic. * Outbound traffic is sent by this machine, while inbound traffic is @@ -8245,12 +8322,10 @@ gen_inbound(compiler_state_t *cstate, int dir) } break; -#ifdef HAVE_NET_PFVAR_H case DLT_PFLOG: b0 = gen_cmp(cstate, OR_LINKHDR, offsetof(struct pfloghdr, dir), BPF_B, - (bpf_int32)((dir == 0) ? PF_IN : PF_OUT)); + ((dir == 0) ? PF_IN : PF_OUT)); break; -#endif case DLT_PPP_PPPD: if (dir) { @@ -8312,9 +8387,9 @@ gen_inbound(compiler_state_t *cstate, int dir) * with newer capture APIs, allowing it to be saved * in pcapng files. */ -#if defined(linux) && defined(PF_PACKET) && defined(SO_ATTACH_FILTER) +#if defined(linux) /* - * This is Linux with PF_PACKET support. + * This is Linux; we require PF_PACKET support. * If this is a *live* capture, we can look at * special meta-data in the filter expression; * if it's a savefile, we can't. @@ -8323,7 +8398,6 @@ gen_inbound(compiler_state_t *cstate, int dir) /* We have a FILE *, so this is a savefile */ bpf_error(cstate, "inbound/outbound not supported on %s when reading savefiles", pcap_datalink_val_to_description_or_dlt(cstate->linktype)); - b0 = NULL; /*NOTREACHED*/ } /* match outgoing packets */ @@ -8333,16 +8407,15 @@ gen_inbound(compiler_state_t *cstate, int dir) /* to filter on inbound traffic, invert the match */ gen_not(b0); } -#else /* defined(linux) && defined(PF_PACKET) && defined(SO_ATTACH_FILTER) */ +#else /* defined(linux) */ bpf_error(cstate, "inbound/outbound not supported on %s", pcap_datalink_val_to_description_or_dlt(cstate->linktype)); /*NOTREACHED*/ -#endif /* defined(linux) && defined(PF_PACKET) && defined(SO_ATTACH_FILTER) */ +#endif /* defined(linux) */ } return (b0); } -#ifdef HAVE_NET_PFVAR_H /* PF firewall log matched interface */ struct block * gen_pf_ifname(compiler_state_t *cstate, const char *ifname) @@ -8421,7 +8494,7 @@ gen_pf_rnr(compiler_state_t *cstate, int rnr) } b0 = gen_cmp(cstate, OR_LINKHDR, offsetof(struct pfloghdr, rulenr), BPF_W, - (bpf_int32)rnr); + (bpf_u_int32)rnr); return (b0); } @@ -8444,7 +8517,7 @@ gen_pf_srnr(compiler_state_t *cstate, int srnr) } b0 = gen_cmp(cstate, OR_LINKHDR, offsetof(struct pfloghdr, subrulenr), BPF_W, - (bpf_int32)srnr); + (bpf_u_int32)srnr); return (b0); } @@ -8467,7 +8540,7 @@ gen_pf_reason(compiler_state_t *cstate, int reason) } b0 = gen_cmp(cstate, OR_LINKHDR, offsetof(struct pfloghdr, reason), BPF_B, - (bpf_int32)reason); + (bpf_u_int32)reason); return (b0); } @@ -8490,98 +8563,13 @@ gen_pf_action(compiler_state_t *cstate, int action) } b0 = gen_cmp(cstate, OR_LINKHDR, offsetof(struct pfloghdr, action), BPF_B, - (bpf_int32)action); + (bpf_u_int32)action); return (b0); } -#else /* !HAVE_NET_PFVAR_H */ -struct block * -gen_pf_ifname(compiler_state_t *cstate, const char *ifname _U_) -{ - /* - * Catch errors reported by us and routines below us, and return NULL - * on an error. - */ - if (setjmp(cstate->top_ctx)) - return (NULL); - - bpf_error(cstate, "libpcap was compiled without pf support"); - /*NOTREACHED*/ -} - -struct block * -gen_pf_ruleset(compiler_state_t *cstate, char *ruleset _U_) -{ - /* - * Catch errors reported by us and routines below us, and return NULL - * on an error. - */ - if (setjmp(cstate->top_ctx)) - return (NULL); - - bpf_error(cstate, "libpcap was compiled on a machine without pf support"); - /*NOTREACHED*/ -} - -struct block * -gen_pf_rnr(compiler_state_t *cstate, int rnr _U_) -{ - /* - * Catch errors reported by us and routines below us, and return NULL - * on an error. - */ - if (setjmp(cstate->top_ctx)) - return (NULL); - - bpf_error(cstate, "libpcap was compiled on a machine without pf support"); - /*NOTREACHED*/ -} - -struct block * -gen_pf_srnr(compiler_state_t *cstate, int srnr _U_) -{ - /* - * Catch errors reported by us and routines below us, and return NULL - * on an error. - */ - if (setjmp(cstate->top_ctx)) - return (NULL); - - bpf_error(cstate, "libpcap was compiled on a machine without pf support"); - /*NOTREACHED*/ -} - -struct block * -gen_pf_reason(compiler_state_t *cstate, int reason _U_) -{ - /* - * Catch errors reported by us and routines below us, and return NULL - * on an error. - */ - if (setjmp(cstate->top_ctx)) - return (NULL); - - bpf_error(cstate, "libpcap was compiled on a machine without pf support"); - /*NOTREACHED*/ -} - -struct block * -gen_pf_action(compiler_state_t *cstate, int action _U_) -{ - /* - * Catch errors reported by us and routines below us, and return NULL - * on an error. - */ - if (setjmp(cstate->top_ctx)) - return (NULL); - - bpf_error(cstate, "libpcap was compiled on a machine without pf support"); - /*NOTREACHED*/ -} -#endif /* HAVE_NET_PFVAR_H */ /* IEEE 802.11 wireless header */ struct block * -gen_p80211_type(compiler_state_t *cstate, int type, int mask) +gen_p80211_type(compiler_state_t *cstate, bpf_u_int32 type, bpf_u_int32 mask) { struct block *b0; @@ -8598,8 +8586,7 @@ gen_p80211_type(compiler_state_t *cstate, int type, int mask) case DLT_PRISM_HEADER: case DLT_IEEE802_11_RADIO_AVS: case DLT_IEEE802_11_RADIO: - b0 = gen_mcmp(cstate, OR_LINKHDR, 0, BPF_B, (bpf_int32)type, - (bpf_int32)mask); + b0 = gen_mcmp(cstate, OR_LINKHDR, 0, BPF_B, type, mask); break; default: @@ -8611,7 +8598,7 @@ gen_p80211_type(compiler_state_t *cstate, int type, int mask) } struct block * -gen_p80211_fcdir(compiler_state_t *cstate, int fcdir) +gen_p80211_fcdir(compiler_state_t *cstate, bpf_u_int32 fcdir) { struct block *b0; @@ -8635,8 +8622,8 @@ gen_p80211_fcdir(compiler_state_t *cstate, int fcdir) /*NOTREACHED*/ } - b0 = gen_mcmp(cstate, OR_LINKHDR, 1, BPF_B, (bpf_int32)fcdir, - (bpf_u_int32)IEEE80211_FC1_DIR_MASK); + b0 = gen_mcmp(cstate, OR_LINKHDR, 1, BPF_B, fcdir, + IEEE80211_FC1_DIR_MASK); return (b0); } @@ -8735,7 +8722,7 @@ gen_vlan_tpid_test(compiler_state_t *cstate) { struct block *b0, *b1; - /* check for VLAN, including QinQ */ + /* check for VLAN, including 802.1ad and QinQ */ b0 = gen_linktype(cstate, ETHERTYPE_8021Q); b1 = gen_linktype(cstate, ETHERTYPE_8021AD); gen_or(b0,b1); @@ -8753,7 +8740,7 @@ gen_vlan_vid_test(compiler_state_t *cstate, bpf_u_int32 vlan_num) bpf_error(cstate, "VLAN tag %u greater than maximum %u", vlan_num, 0x0fff); } - return gen_mcmp(cstate, OR_LINKPL, 0, BPF_H, (bpf_int32)vlan_num, 0x0fff); + return gen_mcmp(cstate, OR_LINKPL, 0, BPF_H, vlan_num, 0x0fff); } static struct block * @@ -8783,7 +8770,8 @@ gen_vlan_no_bpf_extensions(compiler_state_t *cstate, bpf_u_int32 vlan_num, #if defined(SKF_AD_VLAN_TAG_PRESENT) /* add v to variable part of off */ static void -gen_vlan_vloffset_add(compiler_state_t *cstate, bpf_abs_offset *off, int v, struct slist *s) +gen_vlan_vloffset_add(compiler_state_t *cstate, bpf_abs_offset *off, + bpf_u_int32 v, struct slist *s) { struct slist *s2; @@ -8889,7 +8877,7 @@ gen_vlan_bpf_extensions(compiler_state_t *cstate, bpf_u_int32 vlan_num, /* * This is tricky. We need to insert the statements updating variable - * parts of offsets before the the traditional TPID and VID tests so + * parts of offsets before the traditional TPID and VID tests so * that they are called whenever SKF_AD_VLAN_TAG_PRESENT fails but * we do not want this update to affect those checks. That's why we * generate both test blocks first and insert the statements updating @@ -9041,6 +9029,7 @@ gen_mpls(compiler_state_t *cstate, bpf_u_int32 label_num_arg, switch (cstate->linktype) { case DLT_C_HDLC: /* fall through */ + case DLT_HDLC: case DLT_EN10MB: case DLT_NETANALYZER: case DLT_NETANALYZER_TRANSPARENT: @@ -9069,7 +9058,7 @@ gen_mpls(compiler_state_t *cstate, bpf_u_int32 label_num_arg, label_num, 0xFFFFF); } label_num = label_num << 12; /* label is shifted 12 bits on the wire */ - b1 = gen_mcmp(cstate, OR_LINKPL, 0, BPF_W, (bpf_int32)label_num, + b1 = gen_mcmp(cstate, OR_LINKPL, 0, BPF_W, label_num, 0xfffff000); /* only compare the first 20 bits */ gen_and(b0, b1); b0 = b1; @@ -9109,7 +9098,7 @@ gen_pppoed(compiler_state_t *cstate) return (NULL); /* check for PPPoE discovery */ - return gen_linktype(cstate, (bpf_int32)ETHERTYPE_PPPOED); + return gen_linktype(cstate, ETHERTYPE_PPPOED); } struct block * @@ -9127,7 +9116,7 @@ gen_pppoes(compiler_state_t *cstate, bpf_u_int32 sess_num, int has_sess_num) /* * Test against the PPPoE session link-layer type. */ - b0 = gen_linktype(cstate, (bpf_int32)ETHERTYPE_PPPOES); + b0 = gen_linktype(cstate, ETHERTYPE_PPPOES); /* If a specific session is requested, check PPPoE session id */ if (has_sess_num) { @@ -9135,8 +9124,7 @@ gen_pppoes(compiler_state_t *cstate, bpf_u_int32 sess_num, int has_sess_num) bpf_error(cstate, "PPPoE session number %u greater than maximum %u", sess_num, 0x0000ffff); } - b1 = gen_mcmp(cstate, OR_LINKPL, 0, BPF_W, - (bpf_int32)sess_num, 0x0000ffff); + b1 = gen_mcmp(cstate, OR_LINKPL, 0, BPF_W, sess_num, 0x0000ffff); gen_and(b0, b1); b0 = b1; } @@ -9176,7 +9164,7 @@ gen_pppoes(compiler_state_t *cstate, bpf_u_int32 sess_num, int has_sess_num) * specified. Parameterized to handle both IPv4 and IPv6. */ static struct block * gen_geneve_check(compiler_state_t *cstate, - struct block *(*gen_portfn)(compiler_state_t *, int, int, int), + struct block *(*gen_portfn)(compiler_state_t *, u_int, int, int), enum e_offrel offrel, bpf_u_int32 vni, int has_vni) { struct block *b0, *b1; @@ -9186,7 +9174,7 @@ gen_geneve_check(compiler_state_t *cstate, /* Check that we are operating on version 0. Otherwise, we * can't decode the rest of the fields. The version is 2 bits * in the first byte of the Geneve header. */ - b1 = gen_mcmp(cstate, offrel, 8, BPF_B, (bpf_int32)0, 0xc0); + b1 = gen_mcmp(cstate, offrel, 8, BPF_B, 0, 0xc0); gen_and(b0, b1); b0 = b1; @@ -9196,8 +9184,7 @@ gen_geneve_check(compiler_state_t *cstate, vni, 0xffffff); } vni <<= 8; /* VNI is in the upper 3 bytes */ - b1 = gen_mcmp(cstate, offrel, 12, BPF_W, (bpf_int32)vni, - 0xffffff00); + b1 = gen_mcmp(cstate, offrel, 12, BPF_W, vni, 0xffffff00); gen_and(b0, b1); b0 = b1; } @@ -9480,7 +9467,7 @@ gen_geneve_ll_check(compiler_state_t *cstate) static struct block * gen_atmfield_code_internal(compiler_state_t *cstate, int atmfield, - bpf_int32 jvalue, bpf_u_int32 jtype, int reverse) + bpf_u_int32 jvalue, int jtype, int reverse) { struct block *b0; @@ -9491,8 +9478,8 @@ gen_atmfield_code_internal(compiler_state_t *cstate, int atmfield, bpf_error(cstate, "'vpi' supported only on raw ATM"); if (cstate->off_vpi == OFFSET_NOT_SET) abort(); - b0 = gen_ncmp(cstate, OR_LINKHDR, cstate->off_vpi, BPF_B, 0xffffffff, jtype, - reverse, jvalue); + b0 = gen_ncmp(cstate, OR_LINKHDR, cstate->off_vpi, BPF_B, + 0xffffffffU, jtype, reverse, jvalue); break; case A_VCI: @@ -9500,22 +9487,22 @@ gen_atmfield_code_internal(compiler_state_t *cstate, int atmfield, bpf_error(cstate, "'vci' supported only on raw ATM"); if (cstate->off_vci == OFFSET_NOT_SET) abort(); - b0 = gen_ncmp(cstate, OR_LINKHDR, cstate->off_vci, BPF_H, 0xffffffff, jtype, - reverse, jvalue); + b0 = gen_ncmp(cstate, OR_LINKHDR, cstate->off_vci, BPF_H, + 0xffffffffU, jtype, reverse, jvalue); break; case A_PROTOTYPE: if (cstate->off_proto == OFFSET_NOT_SET) abort(); /* XXX - this isn't on FreeBSD */ - b0 = gen_ncmp(cstate, OR_LINKHDR, cstate->off_proto, BPF_B, 0x0f, jtype, - reverse, jvalue); + b0 = gen_ncmp(cstate, OR_LINKHDR, cstate->off_proto, BPF_B, + 0x0fU, jtype, reverse, jvalue); break; case A_MSGTYPE: if (cstate->off_payload == OFFSET_NOT_SET) abort(); b0 = gen_ncmp(cstate, OR_LINKHDR, cstate->off_payload + MSG_TYPE_POS, BPF_B, - 0xffffffff, jtype, reverse, jvalue); + 0xffffffffU, jtype, reverse, jvalue); break; case A_CALLREFTYPE: @@ -9523,8 +9510,8 @@ gen_atmfield_code_internal(compiler_state_t *cstate, int atmfield, bpf_error(cstate, "'callref' supported only on raw ATM"); if (cstate->off_proto == OFFSET_NOT_SET) abort(); - b0 = gen_ncmp(cstate, OR_LINKHDR, cstate->off_proto, BPF_B, 0xffffffff, - jtype, reverse, jvalue); + b0 = gen_ncmp(cstate, OR_LINKHDR, cstate->off_proto, BPF_B, + 0xffffffffU, jtype, reverse, jvalue); break; default: @@ -9567,7 +9554,7 @@ gen_atmtype_llc(compiler_state_t *cstate) struct block * gen_atmfield_code(compiler_state_t *cstate, int atmfield, - bpf_int32 jvalue, bpf_u_int32 jtype, int reverse) + bpf_u_int32 jvalue, int jtype, int reverse) { /* * Catch errors reported by us and routines below us, and return NULL @@ -9707,7 +9694,8 @@ gen_mtp2type_abbrev(compiler_state_t *cstate, int type) (cstate->linktype != DLT_MTP2_WITH_PHDR) ) bpf_error(cstate, "'fisu' supported only on MTP2"); /* gen_ncmp(cstate, offrel, offset, size, mask, jtype, reverse, value) */ - b0 = gen_ncmp(cstate, OR_PACKET, cstate->off_li, BPF_B, 0x3f, BPF_JEQ, 0, 0); + b0 = gen_ncmp(cstate, OR_PACKET, cstate->off_li, BPF_B, + 0x3fU, BPF_JEQ, 0, 0U); break; case M_LSSU: @@ -9715,8 +9703,10 @@ gen_mtp2type_abbrev(compiler_state_t *cstate, int type) (cstate->linktype != DLT_ERF) && (cstate->linktype != DLT_MTP2_WITH_PHDR) ) bpf_error(cstate, "'lssu' supported only on MTP2"); - b0 = gen_ncmp(cstate, OR_PACKET, cstate->off_li, BPF_B, 0x3f, BPF_JGT, 1, 2); - b1 = gen_ncmp(cstate, OR_PACKET, cstate->off_li, BPF_B, 0x3f, BPF_JGT, 0, 0); + b0 = gen_ncmp(cstate, OR_PACKET, cstate->off_li, BPF_B, + 0x3fU, BPF_JGT, 1, 2U); + b1 = gen_ncmp(cstate, OR_PACKET, cstate->off_li, BPF_B, + 0x3fU, BPF_JGT, 0, 0U); gen_and(b1, b0); break; @@ -9725,7 +9715,8 @@ gen_mtp2type_abbrev(compiler_state_t *cstate, int type) (cstate->linktype != DLT_ERF) && (cstate->linktype != DLT_MTP2_WITH_PHDR) ) bpf_error(cstate, "'msu' supported only on MTP2"); - b0 = gen_ncmp(cstate, OR_PACKET, cstate->off_li, BPF_B, 0x3f, BPF_JGT, 0, 2); + b0 = gen_ncmp(cstate, OR_PACKET, cstate->off_li, BPF_B, + 0x3fU, BPF_JGT, 0, 2U); break; case MH_FISU: @@ -9734,7 +9725,8 @@ gen_mtp2type_abbrev(compiler_state_t *cstate, int type) (cstate->linktype != DLT_MTP2_WITH_PHDR) ) bpf_error(cstate, "'hfisu' supported only on MTP2_HSL"); /* gen_ncmp(cstate, offrel, offset, size, mask, jtype, reverse, value) */ - b0 = gen_ncmp(cstate, OR_PACKET, cstate->off_li_hsl, BPF_H, 0xff80, BPF_JEQ, 0, 0); + b0 = gen_ncmp(cstate, OR_PACKET, cstate->off_li_hsl, BPF_H, + 0xff80U, BPF_JEQ, 0, 0U); break; case MH_LSSU: @@ -9742,8 +9734,10 @@ gen_mtp2type_abbrev(compiler_state_t *cstate, int type) (cstate->linktype != DLT_ERF) && (cstate->linktype != DLT_MTP2_WITH_PHDR) ) bpf_error(cstate, "'hlssu' supported only on MTP2_HSL"); - b0 = gen_ncmp(cstate, OR_PACKET, cstate->off_li_hsl, BPF_H, 0xff80, BPF_JGT, 1, 0x0100); - b1 = gen_ncmp(cstate, OR_PACKET, cstate->off_li_hsl, BPF_H, 0xff80, BPF_JGT, 0, 0); + b0 = gen_ncmp(cstate, OR_PACKET, cstate->off_li_hsl, BPF_H, + 0xff80U, BPF_JGT, 1, 0x0100U); + b1 = gen_ncmp(cstate, OR_PACKET, cstate->off_li_hsl, BPF_H, + 0xff80U, BPF_JGT, 0, 0U); gen_and(b1, b0); break; @@ -9752,7 +9746,8 @@ gen_mtp2type_abbrev(compiler_state_t *cstate, int type) (cstate->linktype != DLT_ERF) && (cstate->linktype != DLT_MTP2_WITH_PHDR) ) bpf_error(cstate, "'hmsu' supported only on MTP2_HSL"); - b0 = gen_ncmp(cstate, OR_PACKET, cstate->off_li_hsl, BPF_H, 0xff80, BPF_JGT, 0, 0x0100); + b0 = gen_ncmp(cstate, OR_PACKET, cstate->off_li_hsl, BPF_H, + 0xff80U, BPF_JGT, 0, 0x0100U); break; default: @@ -9768,7 +9763,7 @@ gen_mtp2type_abbrev(compiler_state_t *cstate, int type) */ struct block * gen_mtp3field_code(compiler_state_t *cstate, int mtp3field, - bpf_u_int32 jvalue_arg, bpf_u_int32 jtype, int reverse) + bpf_u_int32 jvalue_arg, int jtype, int reverse) { volatile bpf_u_int32 jvalue = jvalue_arg; struct block *b0; @@ -9802,8 +9797,8 @@ gen_mtp3field_code(compiler_state_t *cstate, int mtp3field, if(jvalue > 255) bpf_error(cstate, "sio value %u too big; max value = 255", jvalue); - b0 = gen_ncmp(cstate, OR_PACKET, newoff_sio, BPF_B, 0xffffffff, - (u_int)jtype, reverse, (u_int)jvalue); + b0 = gen_ncmp(cstate, OR_PACKET, newoff_sio, BPF_B, 0xffffffffU, + jtype, reverse, jvalue); break; case MH_OPC: @@ -9826,8 +9821,8 @@ gen_mtp3field_code(compiler_state_t *cstate, int mtp3field, val3 = jvalue & 0x00000003; val3 = val3 <<22; jvalue = val1 + val2 + val3; - b0 = gen_ncmp(cstate, OR_PACKET, newoff_opc, BPF_W, 0x00c0ff0f, - (u_int)jtype, reverse, (u_int)jvalue); + b0 = gen_ncmp(cstate, OR_PACKET, newoff_opc, BPF_W, 0x00c0ff0fU, + jtype, reverse, jvalue); break; case MH_DPC: @@ -9848,8 +9843,8 @@ gen_mtp3field_code(compiler_state_t *cstate, int mtp3field, val2 = jvalue & 0x00003f00; val2 = val2 << 8; jvalue = val1 + val2; - b0 = gen_ncmp(cstate, OR_PACKET, newoff_dpc, BPF_W, 0xff3f0000, - (u_int)jtype, reverse, (u_int)jvalue); + b0 = gen_ncmp(cstate, OR_PACKET, newoff_dpc, BPF_W, 0xff3f0000U, + jtype, reverse, jvalue); break; case MH_SLS: @@ -9866,8 +9861,8 @@ gen_mtp3field_code(compiler_state_t *cstate, int mtp3field, /* the following instruction is made to convert jvalue * to the forme used to write sls in an ss7 message*/ jvalue = jvalue << 4; - b0 = gen_ncmp(cstate, OR_PACKET, newoff_sls, BPF_B, 0xf0, - (u_int)jtype,reverse, (u_int)jvalue); + b0 = gen_ncmp(cstate, OR_PACKET, newoff_sls, BPF_B, 0xf0U, + jtype, reverse, jvalue); break; default: diff --git a/gencode.h b/gencode.h index cc21e04384a6..93ca5216b556 100644 --- a/gencode.h +++ b/gencode.h @@ -19,7 +19,19 @@ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ +#ifndef gencode_h +#define gencode_h + #include "pcap/funcattrs.h" +/* + * pcap/bpf.h (a public header) needs u_char, u_short and u_int, which can be + * made available via either pcap-types.h (a private header) or pcap/pcap.h + * (a public header), none of which pcap/bpf.h includes. Include the private + * header to keep things simple, this way this private header should compile + * even if included early from another file. + */ +#include "pcap-types.h" +#include "pcap/bpf.h" /* bpf_u_int32 and BPF_MEMWORDS */ /* * ATM support: @@ -200,11 +212,14 @@ struct slist; +/* + * A single statement, corresponding to an instruction in a block. + */ struct stmt { - int code; - struct slist *jt; /*only for relative jump in block*/ - struct slist *jf; /*only for relative jump in block*/ - bpf_int32 k; + int code; /* opcode */ + struct slist *jt; /* only for relative jump in block */ + struct slist *jf; /* only for relative jump in block */ + bpf_u_int32 k; /* k field */ }; struct slist { @@ -231,17 +246,27 @@ typedef bpf_u_int32 *uset; */ #define N_ATOMS (BPF_MEMWORDS+2) +/* + * Control flow graph of a program. + * This corresponds to an edge in the CFG. + * It's a directed graph, so an edge has a predecessor and a successor. + */ struct edge { - int id; - int code; + u_int id; + int code; /* opcode for branch corresponding to this edge */ uset edom; - struct block *succ; - struct block *pred; + struct block *succ; /* successor vertex */ + struct block *pred; /* predecessor vertex */ struct edge *next; /* link list of incoming edges for a node */ }; +/* + * A block is a vertex in the CFG. + * It has a list of statements, with the final statement being a + * branch to successor blocks. + */ struct block { - int id; + u_int id; struct slist *stmts; /* side effect stmts */ struct stmt s; /* branch stmt */ int mark; @@ -250,18 +275,18 @@ struct block { int level; int offset; int sense; - struct edge et; - struct edge ef; + struct edge et; /* edge corresponding to the jt branch */ + struct edge ef; /* edge corresponding to the jf branch */ struct block *head; struct block *link; /* link field used by optimizer */ uset dom; uset closure; - struct edge *in_edges; + struct edge *in_edges; /* first edge in the set (linked list) of edges with this as a successor */ atomset def, kill; atomset in_use; atomset out_use; - int oval; - int val[N_ATOMS]; + int oval; /* value ID for value tested in branch stmt */ + bpf_u_int32 val[N_ATOMS]; }; /* @@ -286,8 +311,8 @@ struct _compiler_state; typedef struct _compiler_state compiler_state_t; -struct arth *gen_loadi(compiler_state_t *, int); -struct arth *gen_load(compiler_state_t *, int, struct arth *, int); +struct arth *gen_loadi(compiler_state_t *, bpf_u_int32); +struct arth *gen_load(compiler_state_t *, int, struct arth *, bpf_u_int32); struct arth *gen_loadlen(compiler_state_t *); struct arth *gen_neg(compiler_state_t *, struct arth *); struct arth *gen_arth(compiler_state_t *, int, struct arth *, struct arth *); @@ -300,10 +325,10 @@ struct block *gen_scode(compiler_state_t *, const char *, struct qual); struct block *gen_ecode(compiler_state_t *, const char *, struct qual); struct block *gen_acode(compiler_state_t *, const char *, struct qual); struct block *gen_mcode(compiler_state_t *, const char *, const char *, - unsigned int, struct qual); + bpf_u_int32, struct qual); #ifdef INET6 struct block *gen_mcode6(compiler_state_t *, const char *, const char *, - unsigned int, struct qual); + bpf_u_int32, struct qual); #endif struct block *gen_ncode(compiler_state_t *, const char *, bpf_u_int32, struct qual); @@ -312,9 +337,10 @@ struct block *gen_relation(compiler_state_t *, int, struct arth *, struct arth *, int); struct block *gen_less(compiler_state_t *, int); struct block *gen_greater(compiler_state_t *, int); -struct block *gen_byteop(compiler_state_t *, int, int, int); +struct block *gen_byteop(compiler_state_t *, int, int, bpf_u_int32); struct block *gen_broadcast(compiler_state_t *, int); struct block *gen_multicast(compiler_state_t *, int); +struct block *gen_ifindex(compiler_state_t *, int); struct block *gen_inbound(compiler_state_t *, int); struct block *gen_llc(compiler_state_t *); @@ -332,14 +358,14 @@ struct block *gen_pppoes(compiler_state_t *, bpf_u_int32, int); struct block *gen_geneve(compiler_state_t *, bpf_u_int32, int); -struct block *gen_atmfield_code(compiler_state_t *, int, bpf_int32, - bpf_u_int32, int); -struct block *gen_atmtype_abbrev(compiler_state_t *, int type); -struct block *gen_atmmulti_abbrev(compiler_state_t *, int type); +struct block *gen_atmfield_code(compiler_state_t *, int, bpf_u_int32, + int, int); +struct block *gen_atmtype_abbrev(compiler_state_t *, int); +struct block *gen_atmmulti_abbrev(compiler_state_t *, int); -struct block *gen_mtp2type_abbrev(compiler_state_t *, int type); +struct block *gen_mtp2type_abbrev(compiler_state_t *, int); struct block *gen_mtp3field_code(compiler_state_t *, int, bpf_u_int32, - bpf_u_int32, int); + int, int); struct block *gen_pf_ifname(compiler_state_t *, const char *); struct block *gen_pf_rnr(compiler_state_t *, int); @@ -348,8 +374,8 @@ struct block *gen_pf_ruleset(compiler_state_t *, char *); struct block *gen_pf_reason(compiler_state_t *, int); struct block *gen_pf_action(compiler_state_t *, int); -struct block *gen_p80211_type(compiler_state_t *, int, int); -struct block *gen_p80211_fcdir(compiler_state_t *, int); +struct block *gen_p80211_type(compiler_state_t *, bpf_u_int32, bpf_u_int32); +struct block *gen_p80211_fcdir(compiler_state_t *, bpf_u_int32); /* * Representation of a program as a tree of blocks, plus current mark. @@ -386,3 +412,5 @@ int pcap_parse(void *, compiler_state_t *); /* XXX */ #define JT(b) ((b)->et.succ) #define JF(b) ((b)->ef.succ) + +#endif /* gencode_h */ diff --git a/grammar.y b/grammar.y.in similarity index 81% rename from grammar.y rename to grammar.y.in index 32cb19c11bb7..b6a3d18382ee 100644 --- a/grammar.y +++ b/grammar.y.in @@ -1,7 +1,7 @@ /* * We want a reentrant parser. */ -%pure-parser +@REENTRANT_PARSER@ /* * We also want a reentrant scanner, so we have to pass the @@ -18,6 +18,27 @@ %parse-param {void *yyscanner} %lex-param {void *yyscanner} +/* + * According to bison documentation, shift/reduce conflicts are not an issue + * in most parsers as long as the number does not evolve over time: + * https://www.gnu.org/software/bison/manual/html_node/Expect-Decl.html + * So, following the advice use %expect to check the amount of shift/reduce + * warnings. + * + * This doesn't appear to work in Berkeley YACC - 1.9 20170709; it still + * warns of 38 shift/reduce conflicts. + * + * The Berkeley YACC documentation: + * + * https://invisible-island.net/byacc/manpage/yacc.html + * + * claims that "Bison's support for "%expect" is broken in more than one + * release.", but doesn't give details. Hopefully, that only means that + * you get warnings even if you have the expected number of shift/reduce + * conflicts, not that anything else fails. + */ +%expect 38 + /* * And we need to pass the compiler state to the scanner. */ @@ -50,6 +71,13 @@ #include #endif +/* + * grammar.h requires gencode.h and sometimes breaks in a polluted namespace + * (see ftmacros.h), so include it early. + */ +#include "gencode.h" +#include "grammar.h" + #include #ifndef _WIN32 @@ -71,17 +99,11 @@ struct rtentry; #include "pcap-int.h" -#include "gencode.h" -#include "grammar.h" #include "scanner.h" -#ifdef HAVE_NET_PFVAR_H -#include -#include -#include -#endif #include "llc.h" #include "ieee80211.h" +#include "pflog.h" #include #ifdef HAVE_OS_PROTO_H @@ -210,8 +232,17 @@ str2tok(const char *str, const struct tok *toks) int i; for (i = 0; toks[i].s != NULL; i++) { - if (pcap_strcasecmp(toks[i].s, str) == 0) + if (pcap_strcasecmp(toks[i].s, str) == 0) { + /* + * Just in case somebody is using this to + * generate values of -1/0xFFFFFFFF. + * That won't work, as it's indistinguishable + * from an error. + */ + if (toks[i].v == -1) + abort(); return (toks[i].v); + } } return (-1); } @@ -224,60 +255,87 @@ yyerror(void *yyscanner _U_, compiler_state_t *cstate, const char *msg) bpf_set_error(cstate, "can't parse filter expression: %s", msg); } -#ifdef HAVE_NET_PFVAR_H +static const struct tok pflog_reasons[] = { + { PFRES_MATCH, "match" }, + { PFRES_BADOFF, "bad-offset" }, + { PFRES_FRAG, "fragment" }, + { PFRES_SHORT, "short" }, + { PFRES_NORM, "normalize" }, + { PFRES_MEMORY, "memory" }, + { PFRES_TS, "bad-timestamp" }, + { PFRES_CONGEST, "congestion" }, + { PFRES_IPOPTIONS, "ip-option" }, + { PFRES_PROTCKSUM, "proto-cksum" }, + { PFRES_BADSTATE, "state-mismatch" }, + { PFRES_STATEINS, "state-insert" }, + { PFRES_MAXSTATES, "state-limit" }, + { PFRES_SRCLIMIT, "src-limit" }, + { PFRES_SYNPROXY, "synproxy" }, +#if defined(__FreeBSD__) + { PFRES_MAPFAILED, "map-failed" }, +#elif defined(__NetBSD__) + { PFRES_STATELOCKED, "state-locked" }, +#elif defined(__OpenBSD__) + { PFRES_TRANSLATE, "translate" }, + { PFRES_NOROUTE, "no-route" }, +#elif defined(__APPLE__) + { PFRES_DUMMYNET, "dummynet" }, +#endif + { 0, NULL } +}; + static int pfreason_to_num(compiler_state_t *cstate, const char *reason) { - const char *reasons[] = PFRES_NAMES; int i; - for (i = 0; reasons[i]; i++) { - if (pcap_strcasecmp(reason, reasons[i]) == 0) - return (i); - } - bpf_set_error(cstate, "unknown PF reason"); - return (-1); + i = str2tok(reason, pflog_reasons); + if (i == -1) + bpf_set_error(cstate, "unknown PF reason \"%s\"", reason); + return (i); } +static const struct tok pflog_actions[] = { + { PF_PASS, "pass" }, + { PF_PASS, "accept" }, /* alias for "pass" */ + { PF_DROP, "drop" }, + { PF_DROP, "block" }, /* alias for "drop" */ + { PF_SCRUB, "scrub" }, + { PF_NOSCRUB, "noscrub" }, + { PF_NAT, "nat" }, + { PF_NONAT, "nonat" }, + { PF_BINAT, "binat" }, + { PF_NOBINAT, "nobinat" }, + { PF_RDR, "rdr" }, + { PF_NORDR, "nordr" }, + { PF_SYNPROXY_DROP, "synproxy-drop" }, +#if defined(__FreeBSD__) + { PF_DEFER, "defer" }, +#elif defined(__OpenBSD__) + { PF_DEFER, "defer" }, + { PF_MATCH, "match" }, + { PF_DIVERT, "divert" }, + { PF_RT, "rt" }, + { PF_AFRT, "afrt" }, +#elif defined(__APPLE__) + { PF_DUMMYNET, "dummynet" }, + { PF_NODUMMYNET, "nodummynet" }, + { PF_NAT64, "nat64" }, + { PF_NONAT64, "nonat64" }, +#endif + { 0, NULL }, +}; + static int pfaction_to_num(compiler_state_t *cstate, const char *action) { - if (pcap_strcasecmp(action, "pass") == 0 || - pcap_strcasecmp(action, "accept") == 0) - return (PF_PASS); - else if (pcap_strcasecmp(action, "drop") == 0 || - pcap_strcasecmp(action, "block") == 0) - return (PF_DROP); -#if HAVE_PF_NAT_THROUGH_PF_NORDR - else if (pcap_strcasecmp(action, "rdr") == 0) - return (PF_RDR); - else if (pcap_strcasecmp(action, "nat") == 0) - return (PF_NAT); - else if (pcap_strcasecmp(action, "binat") == 0) - return (PF_BINAT); - else if (pcap_strcasecmp(action, "nordr") == 0) - return (PF_NORDR); -#endif - else { - bpf_set_error(cstate, "unknown PF action"); - return (-1); - } -} -#else /* !HAVE_NET_PFVAR_H */ -static int -pfreason_to_num(compiler_state_t *cstate, const char *reason _U_) -{ - bpf_set_error(cstate, "libpcap was compiled on a machine without pf support"); - return (-1); -} + int i; -static int -pfaction_to_num(compiler_state_t *cstate, const char *action _U_) -{ - bpf_set_error(cstate, "libpcap was compiled on a machine without pf support"); - return (-1); + i = str2tok(action, pflog_actions); + if (i == -1) + bpf_set_error(cstate, "unknown PF action \"%s\"", action); + return (i); } -#endif /* HAVE_NET_PFVAR_H */ /* * For calls that might return an "an error occurred" value. @@ -307,7 +365,8 @@ DIAG_OFF_BISON_BYACC %type head %type pqual dqual aqual ndaqual %type arth narth -%type byteop pname pnum relop irelop +%type byteop pname relop irelop +%type pnum %type and or paren not null prog %type other pfvar p80211 pllc %type atmtype atmmultitype @@ -324,6 +383,7 @@ DIAG_OFF_BISON_BYACC %token ATALK AARP DECNET LAT SCA MOPRC MOPDL %token TK_BROADCAST TK_MULTICAST %token NUM INBOUND OUTBOUND +%token IFINDEX %token PF_IFNAME PF_RSET PF_RNR PF_SRNR PF_REASON PF_ACTION %token TYPE SUBTYPE DIR ADDR1 ADDR2 ADDR3 ADDR4 RA TA %token LINK @@ -348,7 +408,8 @@ DIAG_OFF_BISON_BYACC %type ID EID AID %type HID HID6 -%type NUM action reason type subtype type_subtype dir +%type NUM +%type action reason type subtype type_subtype dir %left OR AND %nonassoc '!' @@ -378,7 +439,7 @@ and: AND { $$ = $0; } or: OR { $$ = $0; } ; id: nid - | pnum { CHECK_PTR_VAL(($$.b = gen_ncode(cstate, NULL, (bpf_u_int32)$1, + | pnum { CHECK_PTR_VAL(($$.b = gen_ncode(cstate, NULL, $1, $$.q = $0.q))); } | paren pid ')' { $$ = $2; } ; @@ -392,17 +453,17 @@ nid: ID { CHECK_PTR_VAL($1); CHECK_PTR_VAL(($$.b = gen_scode(cstate, $1, $$. /* Decide how to parse HID based on proto */ $$.q = $0.q; if ($$.q.addr == Q_PORT) { - bpf_set_error(cstate, "'port' modifier applied to ip host"); - YYABORT; + bpf_set_error(cstate, "'port' modifier applied to ip host"); + YYABORT; } else if ($$.q.addr == Q_PORTRANGE) { - bpf_set_error(cstate, "'portrange' modifier applied to ip host"); - YYABORT; + bpf_set_error(cstate, "'portrange' modifier applied to ip host"); + YYABORT; } else if ($$.q.addr == Q_PROTO) { - bpf_set_error(cstate, "'proto' modifier applied to ip host"); - YYABORT; + bpf_set_error(cstate, "'proto' modifier applied to ip host"); + YYABORT; } else if ($$.q.addr == Q_PROTOCHAIN) { - bpf_set_error(cstate, "'protochain' modifier applied to ip host"); - YYABORT; + bpf_set_error(cstate, "'protochain' modifier applied to ip host"); + YYABORT; } CHECK_PTR_VAL(($$.b = gen_ncode(cstate, $1, 0, $$.q))); } @@ -440,7 +501,7 @@ pid: nid | qid and id { gen_and($1.b, $3.b); $$ = $3; } | qid or id { gen_or($1.b, $3.b); $$ = $3; } ; -qid: pnum { CHECK_PTR_VAL(($$.b = gen_ncode(cstate, NULL, (bpf_u_int32)$1, +qid: pnum { CHECK_PTR_VAL(($$.b = gen_ncode(cstate, NULL, $1, $$.q = $0.q))); } | pid ; @@ -514,7 +575,7 @@ pname: LINK { $$ = Q_LINK; } | IGRP { $$ = Q_IGRP; } | PIM { $$ = Q_PIM; } | VRRP { $$ = Q_VRRP; } - | CARP { $$ = Q_CARP; } + | CARP { $$ = Q_CARP; } | ATALK { $$ = Q_ATALK; } | AARP { $$ = Q_AARP; } | DECNET { $$ = Q_DECNET; } @@ -549,14 +610,15 @@ other: pqual TK_BROADCAST { CHECK_PTR_VAL(($$ = gen_broadcast(cstate, $1))); } | CBYTE NUM byteop NUM { CHECK_PTR_VAL(($$ = gen_byteop(cstate, $3, $2, $4))); } | INBOUND { CHECK_PTR_VAL(($$ = gen_inbound(cstate, 0))); } | OUTBOUND { CHECK_PTR_VAL(($$ = gen_inbound(cstate, 1))); } - | VLAN pnum { CHECK_PTR_VAL(($$ = gen_vlan(cstate, (bpf_u_int32)$2, 1))); } + | IFINDEX NUM { CHECK_PTR_VAL(($$ = gen_ifindex(cstate, $2))); } + | VLAN pnum { CHECK_PTR_VAL(($$ = gen_vlan(cstate, $2, 1))); } | VLAN { CHECK_PTR_VAL(($$ = gen_vlan(cstate, 0, 0))); } - | MPLS pnum { CHECK_PTR_VAL(($$ = gen_mpls(cstate, (bpf_u_int32)$2, 1))); } + | MPLS pnum { CHECK_PTR_VAL(($$ = gen_mpls(cstate, $2, 1))); } | MPLS { CHECK_PTR_VAL(($$ = gen_mpls(cstate, 0, 0))); } | PPPOED { CHECK_PTR_VAL(($$ = gen_pppoed(cstate))); } - | PPPOES pnum { CHECK_PTR_VAL(($$ = gen_pppoes(cstate, (bpf_u_int32)$2, 1))); } + | PPPOES pnum { CHECK_PTR_VAL(($$ = gen_pppoes(cstate, $2, 1))); } | PPPOES { CHECK_PTR_VAL(($$ = gen_pppoes(cstate, 0, 0))); } - | GENEVE pnum { CHECK_PTR_VAL(($$ = gen_geneve(cstate, (bpf_u_int32)$2, 1))); } + | GENEVE pnum { CHECK_PTR_VAL(($$ = gen_geneve(cstate, $2, 1))); } | GENEVE { CHECK_PTR_VAL(($$ = gen_geneve(cstate, 0, 0))); } | pfvar { $$ = $1; } | pqual p80211 { $$ = $2; } @@ -586,23 +648,33 @@ p80211: TYPE type SUBTYPE subtype | DIR dir { CHECK_PTR_VAL(($$ = gen_p80211_fcdir(cstate, $2))); } ; -type: NUM +type: NUM { if (($1 & (~IEEE80211_FC0_TYPE_MASK)) != 0) { + bpf_set_error(cstate, "invalid 802.11 type value 0x%02x", $1); + YYABORT; + } + $$ = (int)$1; + } | ID { CHECK_PTR_VAL($1); $$ = str2tok($1, ieee80211_types); if ($$ == -1) { - bpf_set_error(cstate, "unknown 802.11 type name"); - YYABORT; + bpf_set_error(cstate, "unknown 802.11 type name \"%s\"", $1); + YYABORT; } } ; -subtype: NUM +subtype: NUM { if (($1 & (~IEEE80211_FC0_SUBTYPE_MASK)) != 0) { + bpf_set_error(cstate, "invalid 802.11 subtype value 0x%02x", $1); + YYABORT; + } + $$ = (int)$1; + } | ID { const struct tok *types = NULL; int i; CHECK_PTR_VAL($1); for (i = 0;; i++) { - if (ieee80211_type_subtypes[i].tok == NULL) { - /* Ran out of types */ + if (ieee80211_type_subtypes[i].tok == NULL) { + /* Ran out of types */ bpf_set_error(cstate, "unknown 802.11 type"); YYABORT; } @@ -614,7 +686,7 @@ subtype: NUM $$ = str2tok($1, types); if ($$ == -1) { - bpf_set_error(cstate, "unknown 802.11 subtype name"); + bpf_set_error(cstate, "unknown 802.11 subtype name \"%s\"", $1); YYABORT; } } @@ -623,8 +695,8 @@ subtype: NUM type_subtype: ID { int i; CHECK_PTR_VAL($1); for (i = 0;; i++) { - if (ieee80211_type_subtypes[i].tok == NULL) { - /* Ran out of types */ + if (ieee80211_type_subtypes[i].tok == NULL) { + /* Ran out of types */ bpf_set_error(cstate, "unknown 802.11 type name"); YYABORT; } @@ -654,9 +726,9 @@ pllc: LLC { CHECK_PTR_VAL(($$ = gen_llc(cstate))); } } else { subtype = str2tok($2, llc_u_subtypes); if (subtype == -1) { - bpf_set_error(cstate, "unknown LLC type name \"%s\"", $2); - YYABORT; - } + bpf_set_error(cstate, "unknown LLC type name \"%s\"", $2); + YYABORT; + } CHECK_PTR_VAL(($$ = gen_llc_u_subtype(cstate, subtype))); } } @@ -665,7 +737,7 @@ pllc: LLC { CHECK_PTR_VAL(($$ = gen_llc(cstate))); } | LLC PF_RNR { CHECK_PTR_VAL(($$ = gen_llc_s_subtype(cstate, LLC_RNR))); } ; -dir: NUM +dir: NUM { $$ = (int)$1; } | ID { CHECK_PTR_VAL($1); if (pcap_strcasecmp($1, "nods") == 0) $$ = IEEE80211_FC1_DIR_NODS; @@ -743,15 +815,15 @@ atmfield: VPI { $$.atmfieldtype = A_VPI; } | VCI { $$.atmfieldtype = A_VCI; } ; atmvalue: atmfieldvalue - | relop NUM { CHECK_PTR_VAL(($$.b = gen_atmfield_code(cstate, $0.atmfieldtype, (bpf_int32)$2, (bpf_u_int32)$1, 0))); } - | irelop NUM { CHECK_PTR_VAL(($$.b = gen_atmfield_code(cstate, $0.atmfieldtype, (bpf_int32)$2, (bpf_u_int32)$1, 1))); } + | relop NUM { CHECK_PTR_VAL(($$.b = gen_atmfield_code(cstate, $0.atmfieldtype, $2, $1, 0))); } + | irelop NUM { CHECK_PTR_VAL(($$.b = gen_atmfield_code(cstate, $0.atmfieldtype, $2, $1, 1))); } | paren atmlistvalue ')' { $$.b = $2.b; $$.q = qerr; } ; atmfieldvalue: NUM { $$.atmfieldtype = $0.atmfieldtype; if ($$.atmfieldtype == A_VPI || $$.atmfieldtype == A_VCI) - CHECK_PTR_VAL(($$.b = gen_atmfield_code(cstate, $$.atmfieldtype, (bpf_int32) $1, BPF_JEQ, 0))); + CHECK_PTR_VAL(($$.b = gen_atmfield_code(cstate, $$.atmfieldtype, $1, BPF_JEQ, 0))); } ; atmlistvalue: atmfieldvalue @@ -776,8 +848,8 @@ mtp3field: SIO { $$.mtp3fieldtype = M_SIO; } | HSLS { $$.mtp3fieldtype = MH_SLS; } ; mtp3value: mtp3fieldvalue - | relop NUM { CHECK_PTR_VAL(($$.b = gen_mtp3field_code(cstate, $0.mtp3fieldtype, (u_int)$2, (u_int)$1, 0))); } - | irelop NUM { CHECK_PTR_VAL(($$.b = gen_mtp3field_code(cstate, $0.mtp3fieldtype, (u_int)$2, (u_int)$1, 1))); } + | relop NUM { CHECK_PTR_VAL(($$.b = gen_mtp3field_code(cstate, $0.mtp3fieldtype, $2, $1, 0))); } + | irelop NUM { CHECK_PTR_VAL(($$.b = gen_mtp3field_code(cstate, $0.mtp3fieldtype, $2, $1, 1))); } | paren mtp3listvalue ')' { $$.b = $2.b; $$.q = qerr; } ; mtp3fieldvalue: NUM { @@ -790,7 +862,7 @@ mtp3fieldvalue: NUM { $$.mtp3fieldtype == MH_OPC || $$.mtp3fieldtype == MH_DPC || $$.mtp3fieldtype == MH_SLS) - CHECK_PTR_VAL(($$.b = gen_mtp3field_code(cstate, $$.mtp3fieldtype, (u_int) $1, BPF_JEQ, 0))); + CHECK_PTR_VAL(($$.b = gen_mtp3field_code(cstate, $$.mtp3fieldtype, $1, BPF_JEQ, 0))); } ; mtp3listvalue: mtp3fieldvalue diff --git a/ieee80211.h b/ieee80211.h index d79f0f8e3656..473803d8a242 100644 --- a/ieee80211.h +++ b/ieee80211.h @@ -29,7 +29,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD$ + * $FreeBSD: src/sys/net80211/ieee80211.h,v 1.10 2005/07/22 16:55:27 sam Exp $ */ #ifndef _NET80211_IEEE80211_H_ #define _NET80211_IEEE80211_H_ diff --git a/lbl/os-osf4.h b/lbl/os-osf4.h index 055eb80ac623..f461eeaff5e6 100644 --- a/lbl/os-osf4.h +++ b/lbl/os-osf4.h @@ -20,7 +20,7 @@ */ /* Prototypes missing in Digital UNIX 4.x */ -int pcap_snprintf(char *, size_t, const char *, ...); -int pcap_vsnprintf(char *, size_t, const char *, va_list); +int snprintf(char *, size_t, const char *, ...); +int vsnprintf(char *, size_t, const char *, va_list); int pfopen(char *, int); diff --git a/lbl/os-osf5.h b/lbl/os-osf5.h index 5422f18f18a1..52ab1750e1fd 100644 --- a/lbl/os-osf5.h +++ b/lbl/os-osf5.h @@ -21,10 +21,10 @@ /* * Prototypes missing in Tru64 UNIX 5.x - * XXX - "pcap_snprintf()" and "pcap_vsnprintf()" aren't missing, but you have to + * XXX - "snprintf()" and "vsnprintf()" aren't missing, but you have to * #define the right value to get them defined by . */ -int pcap_snprintf(char *, size_t, const char *, ...); -int pcap_vsnprintf(char *, size_t, const char *, va_list); +int snprintf(char *, size_t, const char *, ...); +int vsnprintf(char *, size_t, const char *, va_list); int pfopen(char *, int); diff --git a/lbl/os-solaris2.h b/lbl/os-solaris2.h index a555f5ed3147..22948b4a2695 100644 --- a/lbl/os-solaris2.h +++ b/lbl/os-solaris2.h @@ -21,4 +21,4 @@ /* Prototypes missing in SunOS 5 */ char *strerror(int); -int pcap_snprintf(char *, size_t, const char *, ...); +int snprintf(char *, size_t, const char *, ...); diff --git a/lbl/os-sunos4.h b/lbl/os-sunos4.h index 6353fb09cf6e..ab032ef98dbd 100644 --- a/lbl/os-sunos4.h +++ b/lbl/os-sunos4.h @@ -155,7 +155,7 @@ int sigsetmask(int); struct sigvec; #endif int sigvec(int, struct sigvec *, struct sigvec*); -int pcap_snprintf(char *, size_t, const char *, ...); +int snprintf(char *, size_t, const char *, ...); int socket(int, int, int); int socketpair(int, int, int, int *); int symlink(const char *, const char *); diff --git a/libpcap.pc.in b/libpcap.pc.in index d74cbc55dddd..629e662ab09e 100644 --- a/libpcap.pc.in +++ b/libpcap.pc.in @@ -13,6 +13,7 @@ libdir="@libdir@" Name: libpcap Description: Platform-independent network traffic capture library Version: @PACKAGE_VERSION@ -Libs: -L${libdir} -l@PACKAGE_NAME@ -Libs.private: @LIBS@ +Requires.private: @REQUIRES_PRIVATE@ +Libs: -L${libdir} @RPATH@ -l@PACKAGE_NAME@ +Libs.private: @LIBS_PRIVATE@ Cflags: -I${includedir} diff --git a/missing/asprintf.c b/missing/asprintf.c index 3aa55ed9d65d..b65310e1db21 100644 --- a/missing/asprintf.c +++ b/missing/asprintf.c @@ -20,7 +20,7 @@ pcap_vasprintf(char **strp, const char *format, va_list args) int ret; /* - * XXX - the C99 standard says, in section 7.19.6.5 "Thes + * XXX - the C99 standard says, in section 7.19.6.5 "The * nprintf function": * * The snprintf function is equivalent to fprintf, except that diff --git a/missing/getopt.c b/missing/getopt.c index 7c897c6f5936..c535776d849c 100644 --- a/missing/getopt.c +++ b/missing/getopt.c @@ -80,9 +80,18 @@ getopt(int nargc, char * const *nargv, const char *ostr) place = EMSG; return (-1); } - } /* option letter okay? */ - if ((optopt = (int)*place++) == (int)':' || - !(oli = strchr(ostr, optopt))) { + } + optopt = (int)*place++; + if (optopt == (int)':') { /* option letter okay? */ + if (!*place) + ++optind; + if (opterr && *ostr != ':') + (void)fprintf(stderr, + "%s: illegal option -- %c\n", __progname, optopt); + return (BADCH); + } + oli = strchr(ostr, optopt); + if (!oli) { /* * if the user didn't specify '-' as an option, * assume it means -1. @@ -114,7 +123,7 @@ getopt(int nargc, char * const *nargv, const char *ostr) __progname, optopt); return (BADCH); } - else /* white space */ + else /* white space */ optarg = nargv[optind]; place = EMSG; ++optind; diff --git a/missing/snprintf.c b/missing/snprintf.c deleted file mode 100644 index 672aeb86a6d6..000000000000 --- a/missing/snprintf.c +++ /dev/null @@ -1,631 +0,0 @@ -/* - * Copyright (c) 1995-1999 Kungliga Tekniska Högskolan - * (Royal Institute of Technology, Stockholm, Sweden). - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -/* - * We use this for platforms that don't have snprintf() at all. - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include - -#include "portability.h" - -enum format_flags { - minus_flag = 1, - plus_flag = 2, - space_flag = 4, - alternate_flag = 8, - zero_flag = 16 -}; - -/* - * Common state - */ - -struct state { - unsigned char *str; - unsigned char *s; - unsigned char *theend; - size_t sz; - size_t max_sz; - int (*append_char)(struct state *, unsigned char); - int (*reserve)(struct state *, size_t); - /* XXX - methods */ -}; - -#ifndef HAVE_VSNPRINTF -static int -sn_reserve (struct state *state, size_t n) -{ - return state->s + n > state->theend; -} - -static int -sn_append_char (struct state *state, unsigned char c) -{ - if (sn_reserve (state, 1)) { - return 1; - } else { - *state->s++ = c; - return 0; - } -} -#endif - -#if 0 -static int -as_reserve (struct state *state, size_t n) -{ - if (state->s + n > state->theend) { - int off = state->s - state->str; - unsigned char *tmp; - - if (state->max_sz && state->sz >= state->max_sz) - return 1; - - state->sz = max(state->sz * 2, state->sz + n); - if (state->max_sz) - state->sz = min(state->sz, state->max_sz); - tmp = realloc (state->str, state->sz); - if (tmp == NULL) - return 1; - state->str = tmp; - state->s = state->str + off; - state->theend = state->str + state->sz - 1; - } - return 0; -} - -static int -as_append_char (struct state *state, unsigned char c) -{ - if(as_reserve (state, 1)) - return 1; - else { - *state->s++ = c; - return 0; - } -} -#endif - -static int -append_number(struct state *state, - unsigned long num, unsigned base, char *rep, - int width, int prec, int flags, int minusp) -{ - int len = 0; - int i; - - /* given precision, ignore zero flag */ - if(prec != -1) - flags &= ~zero_flag; - else - prec = 1; - /* zero value with zero precision -> "" */ - if(prec == 0 && num == 0) - return 0; - do{ - if((*state->append_char)(state, rep[num % base])) - return 1; - len++; - num /= base; - }while(num); - prec -= len; - /* pad with prec zeros */ - while(prec-- > 0){ - if((*state->append_char)(state, '0')) - return 1; - len++; - } - /* add length of alternate prefix (added later) to len */ - if(flags & alternate_flag && (base == 16 || base == 8)) - len += base / 8; - /* pad with zeros */ - if(flags & zero_flag){ - width -= len; - if(minusp || (flags & space_flag) || (flags & plus_flag)) - width--; - while(width-- > 0){ - if((*state->append_char)(state, '0')) - return 1; - len++; - } - } - /* add alternate prefix */ - if(flags & alternate_flag && (base == 16 || base == 8)){ - if(base == 16) - if((*state->append_char)(state, rep[10] + 23)) /* XXX */ - return 1; - if((*state->append_char)(state, '0')) - return 1; - } - /* add sign */ - if(minusp){ - if((*state->append_char)(state, '-')) - return 1; - len++; - } else if(flags & plus_flag) { - if((*state->append_char)(state, '+')) - return 1; - len++; - } else if(flags & space_flag) { - if((*state->append_char)(state, ' ')) - return 1; - len++; - } - if(flags & minus_flag) - /* swap before padding with spaces */ - for(i = 0; i < len / 2; i++){ - char c = state->s[-i-1]; - state->s[-i-1] = state->s[-len+i]; - state->s[-len+i] = c; - } - width -= len; - while(width-- > 0){ - if((*state->append_char)(state, ' ')) - return 1; - len++; - } - if(!(flags & minus_flag)) - /* swap after padding with spaces */ - for(i = 0; i < len / 2; i++){ - char c = state->s[-i-1]; - state->s[-i-1] = state->s[-len+i]; - state->s[-len+i] = c; - } - - return 0; -} - -static int -append_string (struct state *state, - unsigned char *arg, - int width, - int prec, - int flags) -{ - if(prec != -1) - width -= prec; - else - width -= strlen((char *)arg); - if(!(flags & minus_flag)) - while(width-- > 0) - if((*state->append_char) (state, ' ')) - return 1; - if (prec != -1) { - while (*arg && prec--) - if ((*state->append_char) (state, *arg++)) - return 1; - } else { - while (*arg) - if ((*state->append_char) (state, *arg++)) - return 1; - } - if(flags & minus_flag) - while(width-- > 0) - if((*state->append_char) (state, ' ')) - return 1; - return 0; -} - -static int -append_char(struct state *state, - unsigned char arg, - int width, - int flags) -{ - while(!(flags & minus_flag) && --width > 0) - if((*state->append_char) (state, ' ')) - return 1; - - if((*state->append_char) (state, arg)) - return 1; - while((flags & minus_flag) && --width > 0) - if((*state->append_char) (state, ' ')) - return 1; - - return 0; -} - -/* - * This can't be made into a function... - */ - -#define PARSE_INT_FORMAT(res, arg, unsig) \ -if (long_flag) \ - res = (unsig long)va_arg(arg, unsig long); \ -else if (short_flag) \ - res = (unsig short)va_arg(arg, unsig int); \ -else \ - res = (unsig int)va_arg(arg, unsig int) - -/* - * zyxprintf - return 0 or -1 - */ - -static int -xyzprintf (struct state *state, const char *char_format, va_list ap) -{ - const unsigned char *format = (const unsigned char *)char_format; - unsigned char c; - - while((c = *format++)) { - if (c == '%') { - int flags = 0; - int width = 0; - int prec = -1; - int long_flag = 0; - int short_flag = 0; - - /* flags */ - while((c = *format++)){ - if(c == '-') - flags |= minus_flag; - else if(c == '+') - flags |= plus_flag; - else if(c == ' ') - flags |= space_flag; - else if(c == '#') - flags |= alternate_flag; - else if(c == '0') - flags |= zero_flag; - else - break; - } - - if((flags & space_flag) && (flags & plus_flag)) - flags ^= space_flag; - - if((flags & minus_flag) && (flags & zero_flag)) - flags ^= zero_flag; - - /* width */ - if (isdigit(c)) - do { - width = width * 10 + c - '0'; - c = *format++; - } while(isdigit(c)); - else if(c == '*') { - width = va_arg(ap, int); - c = *format++; - } - - /* precision */ - if (c == '.') { - prec = 0; - c = *format++; - if (isdigit(c)) - do { - prec = prec * 10 + c - '0'; - c = *format++; - } while(isdigit(c)); - else if (c == '*') { - prec = va_arg(ap, int); - c = *format++; - } - } - - /* size */ - - if (c == 'h') { - short_flag = 1; - c = *format++; - } else if (c == 'l') { - long_flag = 1; - c = *format++; - } - - switch (c) { - case 'c' : - if(append_char(state, va_arg(ap, int), width, flags)) - return -1; - break; - case 's' : - if (append_string(state, - va_arg(ap, unsigned char*), - width, - prec, - flags)) - return -1; - break; - case 'd' : - case 'i' : { - long arg; - unsigned long num; - int minusp = 0; - - PARSE_INT_FORMAT(arg, ap, signed); - - if (arg < 0) { - minusp = 1; - num = -arg; - } else - num = arg; - - if (append_number (state, num, 10, "0123456789", - width, prec, flags, minusp)) - return -1; - break; - } - case 'u' : { - unsigned long arg; - - PARSE_INT_FORMAT(arg, ap, unsigned); - - if (append_number (state, arg, 10, "0123456789", - width, prec, flags, 0)) - return -1; - break; - } - case 'o' : { - unsigned long arg; - - PARSE_INT_FORMAT(arg, ap, unsigned); - - if (append_number (state, arg, 010, "01234567", - width, prec, flags, 0)) - return -1; - break; - } - case 'x' : { - unsigned long arg; - - PARSE_INT_FORMAT(arg, ap, unsigned); - - if (append_number (state, arg, 0x10, "0123456789abcdef", - width, prec, flags, 0)) - return -1; - break; - } - case 'X' :{ - unsigned long arg; - - PARSE_INT_FORMAT(arg, ap, unsigned); - - if (append_number (state, arg, 0x10, "0123456789ABCDEF", - width, prec, flags, 0)) - return -1; - break; - } - case 'p' : { - unsigned long arg = (unsigned long)va_arg(ap, void*); - - if (append_number (state, arg, 0x10, "0123456789ABCDEF", - width, prec, flags, 0)) - return -1; - break; - } - case 'n' : { - int *arg = va_arg(ap, int*); - *arg = state->s - state->str; - break; - } - case '\0' : - --format; - /* FALLTHROUGH */ - case '%' : - if ((*state->append_char)(state, c)) - return -1; - break; - default : - if ( (*state->append_char)(state, '%') - || (*state->append_char)(state, c)) - return -1; - break; - } - } else - if ((*state->append_char) (state, c)) - return -1; - } - return 0; -} - -#ifndef HAVE_SNPRINTF -int -pcap_snprintf (char *str, size_t sz, const char *format, ...) -{ - va_list args; - int ret; - - va_start(args, format); - ret = pcap_vsnprintf (str, sz, format, args); - -#ifdef PARANOIA - { - int ret2; - char *tmp; - - tmp = malloc (sz); - if (tmp == NULL) - abort (); - - ret2 = pcap_vsprintf (tmp, format, args); - if (ret != ret2 || strcmp(str, tmp)) - abort (); - free (tmp); - } -#endif - - va_end(args); - return ret; -} -#endif - -#if 0 -#ifndef HAVE_ASPRINTF -int -asprintf (char **ret, const char *format, ...) -{ - va_list args; - int val; - - va_start(args, format); - val = vasprintf (ret, format, args); - -#ifdef PARANOIA - { - int ret2; - char *tmp; - tmp = malloc (val + 1); - if (tmp == NULL) - abort (); - - ret2 = vsprintf (tmp, format, args); - if (val != ret2 || strcmp(*ret, tmp)) - abort (); - free (tmp); - } -#endif - - va_end(args); - return val; -} -#endif - -#ifndef HAVE_ASNPRINTF -int -pcap_asnprintf (char **ret, size_t max_sz, const char *format, ...) -{ - va_list args; - int val; - - va_start(args, format); - val = pcap_vasnprintf (ret, max_sz, format, args); - va_end(args); - -#ifdef PARANOIA - { - int ret2; - char *tmp; - tmp = malloc (val + 1); - if (tmp == NULL) - abort (); - - va_start(args, format); - ret2 = pcap_vsprintf (tmp, format, args); - va_end(args); - if (val != ret2 || strcmp(*ret, tmp)) - abort (); - free (tmp); - } -#endif - - return val; -} -#endif - -#ifndef HAVE_VASPRINTF -int -pcap_vasprintf (char **ret, const char *format, va_list args) -{ - return pcap_vasnprintf (ret, 0, format, args); -} -#endif - - -#ifndef HAVE_VASNPRINTF -int -pcap_vasnprintf (char **ret, size_t max_sz, const char *format, va_list args) -{ - int st; - size_t len; - struct state state; - - state.max_sz = max_sz; - state.sz = 1; - state.str = malloc(state.sz); - if (state.str == NULL) { - *ret = NULL; - return -1; - } - state.s = state.str; - state.theend = state.s + state.sz - 1; - state.append_char = as_append_char; - state.reserve = as_reserve; - - st = xyzprintf (&state, format, args); - if (st) { - free (state.str); - *ret = NULL; - return -1; - } else { - char *tmp; - - *state.s = '\0'; - len = state.s - state.str; - tmp = realloc (state.str, len+1); - if (tmp == NULL) { - free (state.str); - *ret = NULL; - return -1; - } - *ret = tmp; - return len; - } -} -#endif -#endif - -#ifndef HAVE_VSNPRINTF -int -pcap_vsnprintf (char *str, size_t sz, const char *format, va_list args) -{ - struct state state; - int ret; - unsigned char *ustr = (unsigned char *)str; - - state.max_sz = 0; - state.sz = sz; - state.str = ustr; - state.s = ustr; - state.theend = ustr + sz - 1; - state.append_char = sn_append_char; - state.reserve = sn_reserve; - - ret = xyzprintf (&state, format, args); - *state.s = '\0'; - if (ret) - return sz; - else - return state.s - state.str; -} -#endif - diff --git a/missing/win_asprintf.c b/missing/win_asprintf.c index cce6296065fc..e4bd13c61920 100644 --- a/missing/win_asprintf.c +++ b/missing/win_asprintf.c @@ -23,7 +23,7 @@ pcap_vasprintf(char **strp, const char *format, va_list args) *strp = NULL; return (-1); } - ret = pcap_vsnprintf(str, str_size, format, args); + ret = vsnprintf(str, str_size, format, args); if (ret == -1) { free(str); *strp = NULL; @@ -31,7 +31,7 @@ pcap_vasprintf(char **strp, const char *format, va_list args) } *strp = str; /* - * pcap_vsnprintf() shouldn't truncate the string, as we have + * vsnprintf() shouldn't truncate the string, as we have * allocated a buffer large enough to hold the string, so its * return value should be the number of characters printed. */ diff --git a/missing/win_snprintf.c b/missing/win_snprintf.c deleted file mode 100644 index f42240352920..000000000000 --- a/missing/win_snprintf.c +++ /dev/null @@ -1,43 +0,0 @@ -#include -#include - -#include "portability.h" - -int -pcap_vsnprintf(char *str, size_t str_size, const char *format, va_list args) -{ - int ret; - - ret = _vsnprintf_s(str, str_size, _TRUNCATE, format, args); - - /* - * XXX - _vsnprintf() and _snprintf() do *not* guarantee - * that str is null-terminated, but C99's vsnprintf() - * and snprintf() do, and we want to offer C99 behavior, - * so forcibly null-terminate the string. - * - * We don't, however, offer C99 behavior for the return - * value; _vsnprintf_s() returns -1, not the number of - * characters that would have been put into the buffer - * had it been large enough, if the string is truncated. - * The only way to get that value is to use _vscprintf(); - * getting that count isn't worth the re-formatting. - * - * XXX - does _vsnprintf_s() return -1 on a formatting - * error? - */ - str[str_size - 1] = '\0'; - return (ret); -} - -int -pcap_snprintf(char *str, size_t str_size, const char *format, ...) -{ - va_list args; - int ret; - - va_start(args, format); - ret = pcap_vsnprintf(str, str_size, format, args); - va_end(args); - return (ret); -} diff --git a/mkdep b/mkdep index 1486b185aa43..ef120bdb2b3a 100755 --- a/mkdep +++ b/mkdep @@ -16,7 +16,10 @@ MAKE=Makefile # default makefile name is "Makefile" CC=cc # default C compiler is "cc" DEPENDENCY_CFLAG=-M # default dependency-generation flag is -M +SOURCE_DIRECTORY=. # default source directory is the current directory +# No command-line flags seen yet. +flags="" while : do case "$1" in # -c allows you to specify the C compiler @@ -39,13 +42,29 @@ while : -p) SED='s;\.o;;' shift ;; + + # -s allows you to specify the source directory + -s) + SOURCE_DIRECTORY=$2 + shift; shift ;; + + # -include takes an argument + -include) + flags="$flags $1 $2" + shift; shift ;; + + # other command-line flag + -*) + flags="$flags $1" + shift ;; + *) break ;; esac done if [ $# = 0 ] ; then - echo 'usage: mkdep [-p] [-c cc] [-f makefile] [-m dependency-cflag] [flags] file ...' + echo 'usage: mkdep [-p] [-c cc] [-f makefile] [-m dependency-cflag] [-s source-directory] [flags] file ...' exit 1 fi @@ -76,30 +95,20 @@ _EOF_ # egrep '^#include[ ]*".*"' /dev/null $* | # sed -e 's/:[^"]*"\([^"]*\)".*/: \1/' -e 's/\.c/.o/' | +# +# Construct a list of source files with paths relative to the source directory. +# +sources="" +for srcfile in $* +do + sources="$sources $SOURCE_DIRECTORY/$srcfile" +done + # XXX this doesn't work with things like "-DDECLWAITSTATUS=union\ wait" -$CC $DEPENDENCY_CFLAG $* | +$CC $DEPENDENCY_CFLAG $flags $sources | sed " s; \./; ;g - $SED" | -awk '{ - if ($1 != prev) { - if (rec != "") - print rec; - rec = $0; - prev = $1; - } - else { - if (length(rec $2) > 78) { - print rec; - rec = $0; - } - else - rec = rec " " $2 - } -} -END { - print rec -}' >> $TMP + $SED" >> $TMP cat << _EOF_ >> $TMP diff --git a/msdos/readme.dos b/msdos/readme.dos index b95483fc9b4a..ec056dd0bd61 100644 --- a/msdos/readme.dos +++ b/msdos/readme.dos @@ -24,7 +24,7 @@ Note for djgpp users: If you got the libpcap from the official site www.tcpdump, then that distribution does NOT contain any sources for building 32-bit drivers. Instead get the full version at - http://www.watt-32.net/pcap/libpcap.zip + https://www.watt-32.net/pcap/libpcap.zip and set "USE_32BIT_DRIVERS = 1" in msdos\common.dj. @@ -51,12 +51,12 @@ The following packages and tools must be present for all targets. receive network data. It's mostly used to access the 'hosts' file and other features. Get 'watt32s*.zip' at: - http://www.watt-32.net + https://www.watt-32.net 2. Exception handler and disassember library (libexc.a) is needed if "USE_EXCEPT = 1" in common.dj. Available at: - http://www.watt-32.net/misc/exc_dx07.zip + https://www.watt-32.net/misc/exc_dx07.zip 3. Flex & Bison is used to generate parser for the filter handler pcap_compile: @@ -65,7 +65,7 @@ The following packages and tools must be present for all targets. 4. NASM assembler v 0.98 or later is required when building djgpp and Watcom targets: - http://www.nasm.us/ + https://www.nasm.us/ 5. sed (Stream Editor) is required for doing `make depend'. It's available at: diff --git a/nametoaddr.c b/nametoaddr.c index 34a8b5593a64..55f93897b9f8 100644 --- a/nametoaddr.c +++ b/nametoaddr.c @@ -127,7 +127,6 @@ #include #endif /* _WIN32 */ -#include #include #include #include @@ -135,6 +134,8 @@ #include "pcap-int.h" +#include "diag-control.h" + #include "gencode.h" #include #include "nametoaddr.h" @@ -162,7 +163,23 @@ pcap_nametoaddr(const char *name) bpf_u_int32 **p; struct hostent *hp; + /* + * gethostbyname() is deprecated on Windows, perhaps because + * it's not thread-safe, or because it doesn't support IPv6, + * or both. + * + * We deprecate pcap_nametoaddr() on all platforms because + * it's not thread-safe; we supply it for backwards compatibility, + * so suppress the deprecation warning. We could, I guess, + * use getaddrinfo() and construct the array ourselves, but + * that's probably not worth the effort, as that wouldn't make + * this thread-safe - we can't change the API to require that + * our caller free the address array, so we still have to reuse + * a local array. + */ +DIAG_OFF_DEPRECATION if ((hp = gethostbyname(name)) != NULL) { +DIAG_ON_DEPRECATION #ifndef h_addr hlist[0] = (bpf_u_int32 *)hp->h_addr; NTOHL(hp->h_addr); @@ -200,10 +217,10 @@ pcap_nametoaddrinfo(const char *name) * XXX - not guaranteed to be thread-safe! See below for platforms * on which it is thread-safe and on which it isn't. */ +#if defined(_WIN32) || defined(__CYGWIN__) bpf_u_int32 -pcap_nametonetaddr(const char *name) +pcap_nametonetaddr(const char *name _U_) { -#ifdef _WIN32 /* * There's no "getnetbyname()" on Windows. * @@ -217,7 +234,11 @@ pcap_nametonetaddr(const char *name) * of *UN*X* machines.) */ return 0; -#else +} +#else /* _WIN32 */ +bpf_u_int32 +pcap_nametonetaddr(const char *name) +{ /* * UN*X. */ @@ -291,8 +312,8 @@ pcap_nametonetaddr(const char *name) return np->n_net; else return 0; -#endif /* _WIN32 */ } +#endif /* _WIN32 */ /* * Convert a port name to its port and protocol numbers. @@ -569,28 +590,20 @@ struct eproto { */ PCAP_API struct eproto eproto_db[]; PCAP_API_DEF struct eproto eproto_db[] = { - { "pup", ETHERTYPE_PUP }, - { "xns", ETHERTYPE_NS }, + { "aarp", ETHERTYPE_AARP }, + { "arp", ETHERTYPE_ARP }, + { "atalk", ETHERTYPE_ATALK }, + { "decnet", ETHERTYPE_DN }, { "ip", ETHERTYPE_IP }, #ifdef INET6 { "ip6", ETHERTYPE_IPV6 }, #endif - { "arp", ETHERTYPE_ARP }, - { "rarp", ETHERTYPE_REVARP }, - { "sprite", ETHERTYPE_SPRITE }, + { "lat", ETHERTYPE_LAT }, + { "loopback", ETHERTYPE_LOOPBACK }, { "mopdl", ETHERTYPE_MOPDL }, { "moprc", ETHERTYPE_MOPRC }, - { "decnet", ETHERTYPE_DN }, - { "lat", ETHERTYPE_LAT }, + { "rarp", ETHERTYPE_REVARP }, { "sca", ETHERTYPE_SCA }, - { "lanbridge", ETHERTYPE_LANBRIDGE }, - { "vexp", ETHERTYPE_VEXP }, - { "vprod", ETHERTYPE_VPROD }, - { "atalk", ETHERTYPE_ATALK }, - { "atalkarp", ETHERTYPE_AARP }, - { "loopback", ETHERTYPE_LOOPBACK }, - { "decdts", ETHERTYPE_DECDTS }, - { "decdns", ETHERTYPE_DECDNS }, { (char *)0, 0 } }; @@ -635,9 +648,9 @@ pcap_nametollc(const char *s) static inline u_char xdtoi(u_char c) { - if (isdigit(c)) + if (c >= '0' && c <= '9') return (u_char)(c - '0'); - else if (islower(c)) + else if (c >= 'a' && c <= 'f') return (u_char)(c - 'a' + 10); else return (u_char)(c - 'A' + 10); @@ -716,7 +729,7 @@ pcap_ether_aton(const char *s) if (*s == ':' || *s == '.' || *s == '-') s += 1; d = xdtoi(*s++); - if (isxdigit((unsigned char)*s)) { + if (PCAP_ISXDIGIT(*s)) { d <<= 4; d |= xdtoi(*s++); } @@ -772,9 +785,14 @@ pcap_ether_hostton(const char *name) { register u_char *ap; u_char a[6]; + char namebuf[1024]; + /* + * In AIX 7.1 and 7.2: int ether_hostton(char *, struct ether_addr *); + */ + pcap_strlcpy(namebuf, name, sizeof(namebuf)); ap = NULL; - if (ether_hostton(name, (struct ether_addr *)a) == 0) { + if (ether_hostton(namebuf, (struct ether_addr *)a) == 0) { ap = (u_char *)malloc(6); if (ap != NULL) memcpy((char *)ap, (char *)a, 6); diff --git a/nomkdep b/nomkdep old mode 100644 new mode 100755 diff --git a/optimize.c b/optimize.c index 448452d2831b..0ad0c4163bb0 100644 --- a/optimize.c +++ b/optimize.c @@ -32,13 +32,14 @@ #include #include #include - +#include /* for SIZE_MAX */ #include #include "pcap-int.h" #include "gencode.h" #include "optimize.h" +#include "diag-control.h" #ifdef HAVE_OS_PROTO_H #include "os-proto.h" @@ -103,7 +104,7 @@ pcap_set_print_dot_graph(int value) * Takes a 32-bit integer as an argument. * * If handed a non-zero value, returns the index of the lowest set bit, - * counting upwards fro zero. + * counting upwards from zero. * * If handed zero, the results are platform- and compiler-dependent. * Keep it out of the light, don't give it any water, don't feed it @@ -115,7 +116,7 @@ pcap_set_print_dot_graph(int value) /* * GCC 3.4 and later; we have __builtin_ctz(). */ - #define lowest_set_bit(mask) __builtin_ctz(mask) + #define lowest_set_bit(mask) ((u_int)__builtin_ctz(mask)) #elif defined(_MSC_VER) /* * Visual Studio; we support only 2005 and later, so use @@ -127,7 +128,7 @@ pcap_set_print_dot_graph(int value) #pragma intrinsic(_BitScanForward) #endif -static __forceinline int +static __forceinline u_int lowest_set_bit(int mask) { unsigned long bit; @@ -137,15 +138,15 @@ lowest_set_bit(int mask) * (It's currently not, in MSVC, even on 64-bit platforms, but....) */ if (_BitScanForward(&bit, (unsigned int)mask) == 0) - return -1; /* mask is zero */ - return (int)bit; + abort(); /* mask is zero */ + return (u_int)bit; } #elif defined(MSDOS) && defined(__DJGPP__) /* * MS-DOS with DJGPP, which declares ffs() in , which * we've already included. */ - #define lowest_set_bit(mask) (ffs((mask)) - 1) + #define lowest_set_bit(mask) ((u_int)(ffs((mask)) - 1)) #elif (defined(MSDOS) && defined(__WATCOMC__)) || defined(STRINGS_H_DECLARES_FFS) /* * MS-DOS with Watcom C, which has and declares ffs() there, @@ -153,18 +154,18 @@ lowest_set_bit(int mask) * of the Single UNIX Specification). */ #include - #define lowest_set_bit(mask) (ffs((mask)) - 1) + #define lowest_set_bit(mask) (u_int)((ffs((mask)) - 1)) #else /* * None of the above. * Use a perfect-hash-function-based function. */ -static int +static u_int lowest_set_bit(int mask) { unsigned int v = (unsigned int)mask; - static const int MultiplyDeBruijnBitPosition[32] = { + static const u_int MultiplyDeBruijnBitPosition[32] = { 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 }; @@ -213,17 +214,17 @@ lowest_set_bit(int mask) */ struct valnode { int code; - int v0, v1; - int val; + bpf_u_int32 v0, v1; + int val; /* the value number */ struct valnode *next; }; /* Integer constants mapped with the load immediate opcode. */ -#define K(i) F(opt_state, BPF_LD|BPF_IMM|BPF_W, i, 0L) +#define K(i) F(opt_state, BPF_LD|BPF_IMM|BPF_W, i, 0U) struct vmapinfo { int is_const; - bpf_int32 const_val; + bpf_u_int32 const_val; }; typedef struct { @@ -240,21 +241,34 @@ typedef struct { /* * A flag to indicate that further optimization is needed. * Iterative passes are continued until a given pass yields no - * branch movement. + * code simplification or branch movement. */ int done; - int n_blocks; + /* + * XXX - detect loops that do nothing but repeated AND/OR pullups + * and edge moves. + * If 100 passes in a row do nothing but that, treat that as a + * sign that we're in a loop that just shuffles in a cycle in + * which each pass just shuffles the code and we eventually + * get back to the original configuration. + * + * XXX - we need a non-heuristic way of detecting, or preventing, + * such a cycle. + */ + int non_branch_movement_performed; + + u_int n_blocks; /* number of blocks in the CFG; guaranteed to be > 0, as it's a RET instruction at a minimum */ struct block **blocks; - int n_edges; + u_int n_edges; /* twice n_blocks, so guaranteed to be > 0 */ struct edge **edges; /* * A bit vector set representation of the dominators. * We round up the set size to the next power of two. */ - int nodewords; - int edgewords; + u_int nodewords; /* number of 32-bit words for a bit vector of "number of nodes" bits; guaranteed to be > 0 */ + u_int edgewords; /* number of 32-bit words for a bit vector of "number of edges" bits; guaranteed to be > 0 */ struct block **levels; bpf_u_int32 *space; @@ -279,32 +293,35 @@ typedef struct { /* * a := a intersect b + * n must be guaranteed to be > 0 */ #define SET_INTERSECT(a, b, n)\ {\ register bpf_u_int32 *_x = a, *_y = b;\ - register int _n = n;\ - while (--_n >= 0) *_x++ &= *_y++;\ + register u_int _n = n;\ + do *_x++ &= *_y++; while (--_n != 0);\ } /* * a := a - b + * n must be guaranteed to be > 0 */ #define SET_SUBTRACT(a, b, n)\ {\ register bpf_u_int32 *_x = a, *_y = b;\ - register int _n = n;\ - while (--_n >= 0) *_x++ &=~ *_y++;\ + register u_int _n = n;\ + do *_x++ &=~ *_y++; while (--_n != 0);\ } /* * a := a union b + * n must be guaranteed to be > 0 */ #define SET_UNION(a, b, n)\ {\ register bpf_u_int32 *_x = a, *_y = b;\ - register int _n = n;\ - while (--_n >= 0) *_x++ |= *_y++;\ + register u_int _n = n;\ + do *_x++ |= *_y++; while (--_n != 0);\ } uset all_dom_sets; @@ -313,8 +330,8 @@ typedef struct { #define MODULUS 213 struct valnode *hashtbl[MODULUS]; - int curval; - int maxval; + bpf_u_int32 curval; + bpf_u_int32 maxval; struct vmapinfo *vmap; struct valnode *vnode_base; @@ -401,7 +418,8 @@ find_levels(opt_state_t *opt_state, struct icode *ic) static void find_dom(opt_state_t *opt_state, struct block *root) { - int i; + u_int i; + int level; struct block *b; bpf_u_int32 *x; @@ -409,16 +427,23 @@ find_dom(opt_state_t *opt_state, struct block *root) * Initialize sets to contain all nodes. */ x = opt_state->all_dom_sets; + /* + * In opt_init(), we've made sure the product doesn't overflow. + */ i = opt_state->n_blocks * opt_state->nodewords; - while (--i >= 0) + while (i != 0) { + --i; *x++ = 0xFFFFFFFFU; + } /* Root starts off empty. */ - for (i = opt_state->nodewords; --i >= 0;) + for (i = opt_state->nodewords; i != 0;) { + --i; root->dom[i] = 0; + } /* root->level is the highest level no found. */ - for (i = root->level; i >= 0; --i) { - for (b = opt_state->levels[i]; b; b = b->link) { + for (level = root->level; level >= 0; --level) { + for (b = opt_state->levels[level]; b; b = b->link) { SET_INSERT(b->dom, b->id); if (JT(b) == 0) continue; @@ -445,19 +470,25 @@ propedom(opt_state_t *opt_state, struct edge *ep) static void find_edom(opt_state_t *opt_state, struct block *root) { - int i; + u_int i; uset x; + int level; struct block *b; x = opt_state->all_edge_sets; - for (i = opt_state->n_edges * opt_state->edgewords; --i >= 0; ) + /* + * In opt_init(), we've made sure the product doesn't overflow. + */ + for (i = opt_state->n_edges * opt_state->edgewords; i != 0; ) { + --i; x[i] = 0xFFFFFFFFU; + } /* root->level is the highest level no found. */ memset(root->et.edom, 0, opt_state->edgewords * sizeof(*(uset)0)); memset(root->ef.edom, 0, opt_state->edgewords * sizeof(*(uset)0)); - for (i = root->level; i >= 0; --i) { - for (b = opt_state->levels[i]; b != 0; b = b->link) { + for (level = root->level; level >= 0; --level) { + for (b = opt_state->levels[level]; b != 0; b = b->link) { propedom(opt_state, &b->et); propedom(opt_state, &b->ef); } @@ -474,7 +505,7 @@ find_edom(opt_state_t *opt_state, struct block *root) static void find_closure(opt_state_t *opt_state, struct block *root) { - int i; + int level; struct block *b; /* @@ -484,8 +515,8 @@ find_closure(opt_state_t *opt_state, struct block *root) opt_state->n_blocks * opt_state->nodewords * sizeof(*opt_state->all_closure_sets)); /* root->level is the highest level no found. */ - for (i = root->level; i >= 0; --i) { - for (b = opt_state->levels[i]; b; b = b->link) { + for (level = root->level; level >= 0; --level) { + for (b = opt_state->levels[level]; b; b = b->link) { SET_INSERT(b->closure, b->id); if (JT(b) == 0) continue; @@ -496,8 +527,11 @@ find_closure(opt_state_t *opt_state, struct block *root) } /* - * Return the register number that is used by s. If A and X are both - * used, return AX_ATOM. If no register is used, return -1. + * Return the register number that is used by s. + * + * Returns ATOM_A if A is used, ATOM_X if X is used, AX_ATOM if both A and X + * are used, the scratch memory location's number if a scratch memory + * location is used (e.g., 0 for M[0]), or -1 if none of those are used. * * The implementation should probably change to an array access. */ @@ -517,8 +551,12 @@ atomuse(struct stmt *s) case BPF_LD: case BPF_LDX: + /* + * As there are fewer than 2^31 memory locations, + * s->k should be convertible to int without problems. + */ return (BPF_MODE(c) == BPF_IND) ? X_ATOM : - (BPF_MODE(c) == BPF_MEM) ? s->k : -1; + (BPF_MODE(c) == BPF_MEM) ? (int)s->k : -1; case BPF_ST: return A_ATOM; @@ -676,21 +714,40 @@ init_val(opt_state_t *opt_state) memset((char *)opt_state->hashtbl, 0, sizeof opt_state->hashtbl); } -/* Because we really don't have an IR, this stuff is a little messy. */ -static int -F(opt_state_t *opt_state, int code, int v0, int v1) +/* + * Because we really don't have an IR, this stuff is a little messy. + * + * This routine looks in the table of existing value number for a value + * with generated from an operation with the specified opcode and + * the specified values. If it finds it, it returns its value number, + * otherwise it makes a new entry in the table and returns the + * value number of that entry. + */ +static bpf_u_int32 +F(opt_state_t *opt_state, int code, bpf_u_int32 v0, bpf_u_int32 v1) { u_int hash; - int val; + bpf_u_int32 val; struct valnode *p; - hash = (u_int)code ^ ((u_int)v0 << 4) ^ ((u_int)v1 << 8); + hash = (u_int)code ^ (v0 << 4) ^ (v1 << 8); hash %= MODULUS; for (p = opt_state->hashtbl[hash]; p; p = p->next) if (p->code == code && p->v0 == v0 && p->v1 == v1) return p->val; + /* + * Not found. Allocate a new value, and assign it a new + * value number. + * + * opt_state->curval starts out as 0, which means VAL_UNKNOWN; we + * increment it before using it as the new value number, which + * means we never assign VAL_UNKNOWN. + * + * XXX - unless we overflow, but we probably won't have 2^32-1 + * values; we treat 32 bits as effectively infinite. + */ val = ++opt_state->curval; if (BPF_MODE(code) == BPF_IMM && (BPF_CLASS(code) == BPF_LD || BPF_CLASS(code) == BPF_LDX)) { @@ -709,7 +766,7 @@ F(opt_state_t *opt_state, int code, int v0, int v1) } static inline void -vstore(struct stmt *s, int *valp, int newval, int alter) +vstore(struct stmt *s, bpf_u_int32 *valp, bpf_u_int32 newval, int alter) { if (alter && newval != VAL_UNKNOWN && *valp == newval) s->code = NOP; @@ -722,7 +779,7 @@ vstore(struct stmt *s, int *valp, int newval, int alter) * (Unary operators are handled elsewhere.) */ static void -fold_op(opt_state_t *opt_state, struct stmt *s, int v0, int v1) +fold_op(opt_state_t *opt_state, struct stmt *s, bpf_u_int32 v0, bpf_u_int32 v1) { bpf_u_int32 a, b; @@ -807,6 +864,10 @@ fold_op(opt_state_t *opt_state, struct stmt *s, int v0, int v1) } s->k = a; s->code = BPF_LD|BPF_IMM; + /* + * XXX - optimizer loop detection. + */ + opt_state->non_branch_movement_performed = 1; opt_state->done = 0; } @@ -832,7 +893,7 @@ opt_peep(opt_state_t *opt_state, struct block *b) { struct slist *s; struct slist *next, *last; - int val; + bpf_u_int32 val; s = b->stmts; if (s == 0) @@ -863,6 +924,10 @@ opt_peep(opt_state_t *opt_state, struct block *b) if (s->s.code == BPF_ST && next->s.code == (BPF_LDX|BPF_MEM) && s->s.k == next->s.k) { + /* + * XXX - optimizer loop detection. + */ + opt_state->non_branch_movement_performed = 1; opt_state->done = 0; next->s.code = BPF_MISC|BPF_TAX; } @@ -874,6 +939,10 @@ opt_peep(opt_state_t *opt_state, struct block *b) next->s.code == (BPF_MISC|BPF_TAX)) { s->s.code = BPF_LDX|BPF_IMM; next->s.code = BPF_MISC|BPF_TXA; + /* + * XXX - optimizer loop detection. + */ + opt_state->non_branch_movement_performed = 1; opt_state->done = 0; } /* @@ -953,6 +1022,10 @@ opt_peep(opt_state_t *opt_state, struct block *b) s->s.code = NOP; add->s.code = NOP; tax->s.code = NOP; + /* + * XXX - optimizer loop detection. + */ + opt_state->non_branch_movement_performed = 1; opt_state->done = 0; } } @@ -965,10 +1038,10 @@ opt_peep(opt_state_t *opt_state, struct block *b) */ if (b->s.code == (BPF_JMP|BPF_JEQ|BPF_K) && !ATOMELEM(b->out_use, A_ATOM)) { - /* - * We can optimize away certain subtractions of the - * X register. - */ + /* + * We can optimize away certain subtractions of the + * X register. + */ if (last->s.code == (BPF_ALU|BPF_SUB|BPF_X)) { val = b->val[X_ATOM]; if (opt_state->vmap[val].is_const) { @@ -983,6 +1056,10 @@ opt_peep(opt_state_t *opt_state, struct block *b) */ b->s.k += opt_state->vmap[val].const_val; last->s.code = NOP; + /* + * XXX - optimizer loop detection. + */ + opt_state->non_branch_movement_performed = 1; opt_state->done = 0; } else if (b->s.k == 0) { /* @@ -996,6 +1073,10 @@ opt_peep(opt_state_t *opt_state, struct block *b) */ last->s.code = NOP; b->s.code = BPF_JMP|BPF_JEQ|BPF_X; + /* + * XXX - optimizer loop detection. + */ + opt_state->non_branch_movement_performed = 1; opt_state->done = 0; } } @@ -1008,6 +1089,10 @@ opt_peep(opt_state_t *opt_state, struct block *b) else if (last->s.code == (BPF_ALU|BPF_SUB|BPF_K)) { last->s.code = NOP; b->s.k += last->s.k; + /* + * XXX - optimizer loop detection. + */ + opt_state->non_branch_movement_performed = 1; opt_state->done = 0; } /* @@ -1022,6 +1107,10 @@ opt_peep(opt_state_t *opt_state, struct block *b) b->s.k = last->s.k; b->s.code = BPF_JMP|BPF_K|BPF_JSET; last->s.code = NOP; + /* + * XXX - optimizer loop detection. + */ + opt_state->non_branch_movement_performed = 1; opt_state->done = 0; opt_not(b); } @@ -1033,7 +1122,7 @@ opt_peep(opt_state_t *opt_state, struct block *b) if (b->s.code == (BPF_JMP|BPF_K|BPF_JSET)) { if (b->s.k == 0) JT(b) = JF(b); - if ((u_int)b->s.k == 0xffffffffU) + if (b->s.k == 0xffffffffU) JF(b) = JT(b); } /* @@ -1043,7 +1132,7 @@ opt_peep(opt_state_t *opt_state, struct block *b) */ val = b->val[X_ATOM]; if (opt_state->vmap[val].is_const && BPF_SRC(b->s.code) == BPF_X) { - bpf_int32 v = opt_state->vmap[val].const_val; + bpf_u_int32 v = opt_state->vmap[val].const_val; b->s.code &= ~BPF_X; b->s.k = v; } @@ -1053,7 +1142,7 @@ opt_peep(opt_state_t *opt_state, struct block *b) */ val = b->val[A_ATOM]; if (opt_state->vmap[val].is_const && BPF_SRC(b->s.code) == BPF_K) { - bpf_int32 v = opt_state->vmap[val].const_val; + bpf_u_int32 v = opt_state->vmap[val].const_val; switch (BPF_OP(b->s.code)) { case BPF_JEQ: @@ -1061,11 +1150,11 @@ opt_peep(opt_state_t *opt_state, struct block *b) break; case BPF_JGT: - v = (unsigned)v > (unsigned)b->s.k; + v = v > b->s.k; break; case BPF_JGE: - v = (unsigned)v >= (unsigned)b->s.k; + v = v >= b->s.k; break; case BPF_JSET: @@ -1075,8 +1164,13 @@ opt_peep(opt_state_t *opt_state, struct block *b) default: abort(); } - if (JF(b) != JT(b)) + if (JF(b) != JT(b)) { + /* + * XXX - optimizer loop detection. + */ + opt_state->non_branch_movement_performed = 1; opt_state->done = 0; + } if (v) JF(b) = JT(b); else @@ -1091,10 +1185,10 @@ opt_peep(opt_state_t *opt_state, struct block *b) * evaluation and code transformations weren't folded together. */ static void -opt_stmt(opt_state_t *opt_state, struct stmt *s, int val[], int alter) +opt_stmt(opt_state_t *opt_state, struct stmt *s, bpf_u_int32 val[], int alter) { int op; - int v; + bpf_u_int32 v; switch (s->code) { @@ -1113,6 +1207,10 @@ opt_stmt(opt_state_t *opt_state, struct stmt *s, int val[], int alter) s->code = BPF_LD|BPF_ABS|BPF_SIZE(s->code); s->k += opt_state->vmap[v].const_val; v = F(opt_state, s->code, s->k, 0L); + /* + * XXX - optimizer loop detection. + */ + opt_state->non_branch_movement_performed = 1; opt_state->done = 0; } else @@ -1159,7 +1257,7 @@ opt_stmt(opt_state_t *opt_state, struct stmt *s, int val[], int alter) * about the result of negating 0x80000000 being * undefined. */ - s->k = 0U - (bpf_u_int32)(opt_state->vmap[val[A_ATOM]].const_val); + s->k = 0U - opt_state->vmap[val[A_ATOM]].const_val; val[A_ATOM] = K(s->k); } else @@ -1236,16 +1334,14 @@ opt_stmt(opt_state_t *opt_state, struct stmt *s, int val[], int alter) else { s->code = BPF_ALU|BPF_K|op; s->k = opt_state->vmap[val[X_ATOM]].const_val; - /* - * XXX - we need to make up our minds - * as to what integers are signed and - * what integers are unsigned in BPF - * programs and in our IR. - */ if ((op == BPF_LSH || op == BPF_RSH) && - (s->k < 0 || s->k > 31)) + s->k > 31) opt_error(opt_state, "shift by more than 31 bits"); + /* + * XXX - optimizer loop detection. + */ + opt_state->non_branch_movement_performed = 1; opt_state->done = 0; val[A_ATOM] = F(opt_state, s->code, val[A_ATOM], K(s->k)); @@ -1290,6 +1386,10 @@ opt_stmt(opt_state_t *opt_state, struct stmt *s, int val[], int alter) if (alter && opt_state->vmap[v].is_const) { s->code = BPF_LD|BPF_IMM; s->k = opt_state->vmap[v].const_val; + /* + * XXX - optimizer loop detection. + */ + opt_state->non_branch_movement_performed = 1; opt_state->done = 0; } vstore(s, &val[A_ATOM], v, alter); @@ -1304,6 +1404,10 @@ opt_stmt(opt_state_t *opt_state, struct stmt *s, int val[], int alter) if (alter && opt_state->vmap[v].is_const) { s->code = BPF_LDX|BPF_IMM; s->k = opt_state->vmap[v].const_val; + /* + * XXX - optimizer loop detection. + */ + opt_state->non_branch_movement_performed = 1; opt_state->done = 0; } vstore(s, &val[X_ATOM], v, alter); @@ -1336,6 +1440,10 @@ deadstmt(opt_state_t *opt_state, register struct stmt *s, register struct stmt * atom = atomdef(s); if (atom >= 0) { if (last[atom]) { + /* + * XXX - optimizer loop detection. + */ + opt_state->non_branch_movement_performed = 1; opt_state->done = 0; last[atom]->code = NOP; } @@ -1359,6 +1467,10 @@ opt_deadstores(opt_state_t *opt_state, register struct block *b) for (atom = 0; atom < N_ATOMS; ++atom) if (last[atom] && !ATOMELEM(b->out_use, atom)) { last[atom]->code = NOP; + /* + * XXX - optimizer loop detection. + */ + opt_state->non_branch_movement_performed = 1; opt_state->done = 0; } } @@ -1369,7 +1481,7 @@ opt_blk(opt_state_t *opt_state, struct block *b, int do_stmts) struct slist *s; struct edge *p; int i; - bpf_int32 aval, xval; + bpf_u_int32 aval, xval; #if 0 for (s = b->stmts; s && s->next; s = s->next) @@ -1422,7 +1534,10 @@ opt_blk(opt_state_t *opt_state, struct block *b, int do_stmts) * value that is already there, or if this block is a return, * eliminate all the statements. * - * XXX - what if it does a store? + * XXX - what if it does a store? Presumably that falls under + * the heading of "if we don't use anything from this block", + * i.e., if we use any memory location set to a different + * value by this block, then we use something from this block. * * XXX - why does it matter whether we use anything from this * block? If the accumulator or index register doesn't change @@ -1446,6 +1561,10 @@ opt_blk(opt_state_t *opt_state, struct block *b, int do_stmts) BPF_CLASS(b->s.code) == BPF_RET)) { if (b->stmts != 0) { b->stmts = 0; + /* + * XXX - optimizer loop detection. + */ + opt_state->non_branch_movement_performed = 1; opt_state->done = 0; } } else { @@ -1484,19 +1603,41 @@ use_conflict(struct block *b, struct block *succ) return 0; } +/* + * Given a block that is the successor of an edge, and an edge that + * dominates that edge, return either a pointer to a child of that + * block (a block to which that block jumps) if that block is a + * candidate to replace the successor of the latter edge or NULL + * if neither of the children of the first block are candidates. + */ static struct block * fold_edge(struct block *child, struct edge *ep) { int sense; - int aval0, aval1, oval0, oval1; + bpf_u_int32 aval0, aval1, oval0, oval1; int code = ep->code; if (code < 0) { + /* + * This edge is a "branch if false" edge. + */ code = -code; sense = 0; - } else + } else { + /* + * This edge is a "branch if true" edge. + */ sense = 1; + } + /* + * If the opcode for the branch at the end of the block we + * were handed isn't the same as the opcode for the branch + * to which the edge we were handed corresponds, the tests + * for those branches aren't testing the same conditions, + * so the blocks to which the first block branches aren't + * candidates to replace the successor of the edge. + */ if (child->s.code != code) return 0; @@ -1505,13 +1646,21 @@ fold_edge(struct block *child, struct edge *ep) aval1 = ep->pred->val[A_ATOM]; oval1 = ep->pred->oval; + /* + * If the A register value on exit from the successor block + * isn't the same as the A register value on exit from the + * predecessor of the edge, the blocks to which the first + * block branches aren't candidates to replace the successor + * of the edge. + */ if (aval0 != aval1) return 0; if (oval0 == oval1) /* * The operands of the branch instructions are - * identical, so the result is true if a true + * identical, so the branches are testing the + * same condition, and the result is true if a true * branch was taken to get here, otherwise false. */ return sense ? JT(child) : JF(child); @@ -1536,21 +1685,58 @@ fold_edge(struct block *child, struct edge *ep) return 0; } +/* + * If we can make this edge go directly to a child of the edge's current + * successor, do so. + */ static void opt_j(opt_state_t *opt_state, struct edge *ep) { - register int i, k; + register u_int i, k; register struct block *target; + /* + * Does this edge go to a block where, if the test + * at the end of it succeeds, it goes to a block + * that's a leaf node of the DAG, i.e. a return + * statement? + * If so, there's nothing to optimize. + */ if (JT(ep->succ) == 0) return; + /* + * Does this edge go to a block that goes, in turn, to + * the same block regardless of whether the test at the + * end succeeds or fails? + */ if (JT(ep->succ) == JF(ep->succ)) { /* * Common branch targets can be eliminated, provided * there is no data dependency. + * + * Check whether any register used on exit from the + * block to which the successor of this edge goes + * has a value at that point that's different from + * the value it has on exit from the predecessor of + * this edge. If not, the predecessor of this edge + * can just go to the block to which the successor + * of this edge goes, bypassing the successor of this + * edge, as the successor of this edge isn't doing + * any calculations whose results are different + * from what the blocks before it did and isn't + * doing any tests the results of which matter. */ - if (!use_conflict(ep->pred, ep->succ->et.succ)) { + if (!use_conflict(ep->pred, JT(ep->succ))) { + /* + * No, there isn't. + * Make this edge go to the block to + * which the successor of that edge + * goes. + * + * XXX - optimizer loop detection. + */ + opt_state->non_branch_movement_performed = 1; opt_state->done = 0; ep->succ = JT(ep->succ); } @@ -1564,19 +1750,38 @@ opt_j(opt_state_t *opt_state, struct edge *ep) */ top: for (i = 0; i < opt_state->edgewords; ++i) { + /* i'th word in the bitset of dominators */ register bpf_u_int32 x = ep->edom[i]; while (x != 0) { + /* Find the next dominator in that word and mark it as found */ k = lowest_set_bit(x); x &=~ ((bpf_u_int32)1 << k); k += i * BITS_PER_WORD; target = fold_edge(ep->succ, opt_state->edges[k]); /* + * We have a candidate to replace the successor + * of ep. + * * Check that there is no data dependency between - * nodes that will be violated if we move the edge. + * nodes that will be violated if we move the edge; + * i.e., if any register used on exit from the + * candidate has a value at that point different + * from the value it has when we exit the + * predecessor of that edge, there's a data + * dependency that will be violated. */ if (target != 0 && !use_conflict(ep->pred, target)) { + /* + * It's safe to replace the successor of + * ep; do so, and note that we've made + * at least one change. + * + * XXX - this is one of the operations that + * happens when the optimizer gets into + * one of those infinite loops. + */ opt_state->done = 0; ep->succ = target; if (JT(target) != 0) @@ -1590,11 +1795,30 @@ opt_j(opt_state_t *opt_state, struct edge *ep) } } - +/* + * XXX - is this, and and_pullup(), what's described in section 6.1.2 + * "Predicate Assertion Propagation" in the BPF+ paper? + * + * Note that this looks at block dominators, not edge dominators. + * Don't think so. + * + * "A or B" compiles into + * + * A + * t / \ f + * / B + * / t / \ f + * \ / + * \ / + * X + * + * + */ static void or_pullup(opt_state_t *opt_state, struct block *b) { - int val, at_top; + bpf_u_int32 val; + int at_top; struct block *pull; struct block **diffp, **samep; struct edge *ep; @@ -1612,39 +1836,106 @@ or_pullup(opt_state_t *opt_state, struct block *b) if (val != ep->pred->val[A_ATOM]) return; + /* + * For the first edge in the list of edges coming into this block, + * see whether the predecessor of that edge comes here via a true + * branch or a false branch. + */ if (JT(b->in_edges->pred) == b) - diffp = &JT(b->in_edges->pred); + diffp = &JT(b->in_edges->pred); /* jt */ else - diffp = &JF(b->in_edges->pred); + diffp = &JF(b->in_edges->pred); /* jf */ + /* + * diffp is a pointer to a pointer to the block. + * + * Go down the false chain looking as far as you can, + * making sure that each jump-compare is doing the + * same as the original block. + * + * If you reach the bottom before you reach a + * different jump-compare, just exit. There's nothing + * to do here. XXX - no, this version is checking for + * the value leaving the block; that's from the BPF+ + * pullup routine. + */ at_top = 1; for (;;) { + /* + * Done if that's not going anywhere XXX + */ if (*diffp == 0) return; + /* + * Done if that predecessor blah blah blah isn't + * going the same place we're going XXX + * + * Does the true edge of this block point to the same + * location as the true edge of b? + */ if (JT(*diffp) != JT(b)) return; + /* + * Done if this node isn't a dominator of that + * node blah blah blah XXX + * + * Does b dominate diffp? + */ if (!SET_MEMBER((*diffp)->dom, b->id)) return; + /* + * Break out of the loop if that node's value of A + * isn't the value of A above XXX + */ if ((*diffp)->val[A_ATOM] != val) break; + /* + * Get the JF for that node XXX + * Go down the false path. + */ diffp = &JF(*diffp); at_top = 0; } + + /* + * Now that we've found a different jump-compare in a chain + * below b, search further down until we find another + * jump-compare that looks at the original value. This + * jump-compare should get pulled up. XXX again we're + * comparing values not jump-compares. + */ samep = &JF(*diffp); for (;;) { + /* + * Done if that's not going anywhere XXX + */ if (*samep == 0) return; + /* + * Done if that predecessor blah blah blah isn't + * going the same place we're going XXX + */ if (JT(*samep) != JT(b)) return; + /* + * Done if this node isn't a dominator of that + * node blah blah blah XXX + * + * Does b dominate samep? + */ if (!SET_MEMBER((*samep)->dom, b->id)) return; + /* + * Break out of the loop if that node's value of A + * is the value of A above XXX + */ if ((*samep)->val[A_ATOM] == val) break; @@ -1680,13 +1971,18 @@ or_pullup(opt_state_t *opt_state, struct block *b) else *diffp = pull; + /* + * XXX - this is one of the operations that happens when the + * optimizer gets into one of those infinite loops. + */ opt_state->done = 0; } static void and_pullup(opt_state_t *opt_state, struct block *b) { - int val, at_top; + bpf_u_int32 val; + int at_top; struct block *pull; struct block **diffp, **samep; struct edge *ep; @@ -1771,6 +2067,10 @@ and_pullup(opt_state_t *opt_state, struct block *b) else *diffp = pull; + /* + * XXX - this is one of the operations that happens when the + * optimizer gets into one of those infinite loops. + */ opt_state->done = 0; } @@ -1792,9 +2092,22 @@ opt_blks(opt_state_t *opt_state, struct icode *ic, int do_stmts) /* * No point trying to move branches; it can't possibly * make a difference at this point. + * + * XXX - this might be after we detect a loop where + * we were just looping infinitely moving branches + * in such a fashion that we went through two or more + * versions of the machine code, eventually returning + * to the first version. (We're really not doing a + * full loop detection, we're just testing for two + * passes in a row where we do nothing but + * move branches.) */ return; + /* + * Is this what the BPF+ paper describes in sections 6.1.1, + * 6.1.2, and 6.1.3? + */ for (i = 1; i <= maxlevel; ++i) { for (p = opt_state->levels[i]; p; p = p->link) { opt_j(opt_state, &p->et); @@ -1821,7 +2134,8 @@ link_inedge(struct edge *parent, struct block *child) static void find_inedges(opt_state_t *opt_state, struct block *root) { - int i; + u_int i; + int level; struct block *b; for (i = 0; i < opt_state->n_blocks; ++i) @@ -1831,8 +2145,8 @@ find_inedges(opt_state_t *opt_state, struct block *root) * Traverse the graph, adding each edge to the predecessor * list of its successors. Skip the leaves (i.e. level 0). */ - for (i = root->level; i > 0; --i) { - for (b = opt_state->levels[i]; b != 0; b = b->link) { + for (level = root->level; level > 0; --level) { + for (b = opt_state->levels[level]; b != 0; b = b->link) { link_inedge(&b->et, JT(b)); link_inedge(&b->ef, JF(b)); } @@ -1873,8 +2187,17 @@ opt_loop(opt_state_t *opt_state, struct icode *ic, int do_stmts) opt_dump(opt_state, ic); } #endif - do { + + /* + * XXX - optimizer loop detection. + */ + int loop_count = 0; + for (;;) { opt_state->done = 1; + /* + * XXX - optimizer loop detection. + */ + opt_state->non_branch_movement_performed = 0; find_levels(opt_state, ic); find_dom(opt_state, ic->root); find_closure(opt_state, ic->root); @@ -1887,7 +2210,51 @@ opt_loop(opt_state_t *opt_state, struct icode *ic, int do_stmts) opt_dump(opt_state, ic); } #endif - } while (!opt_state->done); + + /* + * Was anything done in this optimizer pass? + */ + if (opt_state->done) { + /* + * No, so we've reached a fixed point. + * We're done. + */ + break; + } + + /* + * XXX - was anything done other than branch movement + * in this pass? + */ + if (opt_state->non_branch_movement_performed) { + /* + * Yes. Clear any loop-detection counter; + * we're making some form of progress (assuming + * we can't get into a cycle doing *other* + * optimizations...). + */ + loop_count = 0; + } else { + /* + * No - increment the counter, and quit if + * it's up to 100. + */ + loop_count++; + if (loop_count >= 100) { + /* + * We've done nothing but branch movement + * for 100 passes; we're probably + * in a cycle and will never reach a + * fixed point. + * + * XXX - yes, we really need a non- + * heuristic way of detecting a cycle. + */ + opt_state->done = 1; + break; + } + } + } } /* @@ -1901,6 +2268,7 @@ bpf_optimize(struct icode *ic, char *errbuf) memset(&opt_state, 0, sizeof(opt_state)); opt_state.errbuf = errbuf; + opt_state.non_branch_movement_performed = 0; if (setjmp(opt_state.top_ctx)) { opt_cleanup(&opt_state); return -1; @@ -1987,7 +2355,7 @@ static void intern_blocks(opt_state_t *opt_state, struct icode *ic) { struct block *p; - int i, j; + u_int i, j; int done1; /* don't shadow global */ top: done1 = 1; @@ -1996,7 +2364,8 @@ intern_blocks(opt_state_t *opt_state, struct icode *ic) mark_code(ic); - for (i = opt_state->n_blocks - 1; --i >= 0; ) { + for (i = opt_state->n_blocks - 1; i != 0; ) { + --i; if (!isMarked(ic, opt_state->blocks[i])) continue; for (j = i + 1; j < opt_state->n_blocks; ++j) { @@ -2047,12 +2416,15 @@ opt_error(opt_state_t *opt_state, const char *fmt, ...) if (opt_state->errbuf != NULL) { va_start(ap, fmt); - (void)pcap_vsnprintf(opt_state->errbuf, + (void)vsnprintf(opt_state->errbuf, PCAP_ERRBUF_SIZE, fmt, ap); va_end(ap); } longjmp(opt_state->top_ctx, 1); /* NOTREACHED */ +#ifdef _AIX + PCAP_UNREACHABLE +#endif /* _AIX */ } /* @@ -2089,13 +2461,19 @@ count_blocks(struct icode *ic, struct block *p) static void number_blks_r(opt_state_t *opt_state, struct icode *ic, struct block *p) { - int n; + u_int n; if (p == 0 || isMarked(ic, p)) return; Mark(ic, p); n = opt_state->n_blocks++; + if (opt_state->n_blocks == 0) { + /* + * Overflow. + */ + opt_error(opt_state, "filter is too complex to optimize"); + } p->id = n; opt_state->blocks[n] = p; @@ -2143,6 +2521,8 @@ opt_init(opt_state_t *opt_state, struct icode *ic) { bpf_u_int32 *p; int i, n, max_stmts; + u_int product; + size_t block_memsize, edge_memsize; /* * First, count the blocks, so we can malloc an array to map @@ -2157,7 +2537,19 @@ opt_init(opt_state_t *opt_state, struct icode *ic) opt_state->n_blocks = 0; number_blks_r(opt_state, ic, ic->root); + /* + * This "should not happen". + */ + if (opt_state->n_blocks == 0) + opt_error(opt_state, "filter has no instructions; please report this as a libpcap issue"); + opt_state->n_edges = 2 * opt_state->n_blocks; + if ((opt_state->n_edges / 2) != opt_state->n_blocks) { + /* + * Overflow. + */ + opt_error(opt_state, "filter is too complex to optimize"); + } opt_state->edges = (struct edge **)calloc(opt_state->n_edges, sizeof(*opt_state->edges)); if (opt_state->edges == NULL) { opt_error(opt_state, "malloc"); @@ -2171,12 +2563,62 @@ opt_init(opt_state_t *opt_state, struct icode *ic) opt_error(opt_state, "malloc"); } - opt_state->edgewords = opt_state->n_edges / (8 * sizeof(bpf_u_int32)) + 1; - opt_state->nodewords = opt_state->n_blocks / (8 * sizeof(bpf_u_int32)) + 1; + opt_state->edgewords = opt_state->n_edges / BITS_PER_WORD + 1; + opt_state->nodewords = opt_state->n_blocks / BITS_PER_WORD + 1; + + /* + * Make sure opt_state->n_blocks * opt_state->nodewords fits + * in a u_int; we use it as a u_int number-of-iterations + * value. + */ + product = opt_state->n_blocks * opt_state->nodewords; + if ((product / opt_state->n_blocks) != opt_state->nodewords) { + /* + * XXX - just punt and don't try to optimize? + * In practice, this is unlikely to happen with + * a normal filter. + */ + opt_error(opt_state, "filter is too complex to optimize"); + } + + /* + * Make sure the total memory required for that doesn't + * overflow. + */ + block_memsize = (size_t)2 * product * sizeof(*opt_state->space); + if ((block_memsize / product) != 2 * sizeof(*opt_state->space)) { + opt_error(opt_state, "filter is too complex to optimize"); + } + + /* + * Make sure opt_state->n_edges * opt_state->edgewords fits + * in a u_int; we use it as a u_int number-of-iterations + * value. + */ + product = opt_state->n_edges * opt_state->edgewords; + if ((product / opt_state->n_edges) != opt_state->edgewords) { + opt_error(opt_state, "filter is too complex to optimize"); + } + + /* + * Make sure the total memory required for that doesn't + * overflow. + */ + edge_memsize = (size_t)product * sizeof(*opt_state->space); + if (edge_memsize / product != sizeof(*opt_state->space)) { + opt_error(opt_state, "filter is too complex to optimize"); + } + + /* + * Make sure the total memory required for both of them doesn't + * overflow. + */ + if (block_memsize > SIZE_MAX - edge_memsize) { + opt_error(opt_state, "filter is too complex to optimize"); + } /* XXX */ - opt_state->space = (bpf_u_int32 *)malloc(2 * opt_state->n_blocks * opt_state->nodewords * sizeof(*opt_state->space) - + opt_state->n_edges * opt_state->edgewords * sizeof(*opt_state->space)); + opt_state->space = (bpf_u_int32 *)malloc(block_memsize + edge_memsize); if (opt_state->space == NULL) { opt_error(opt_state, "malloc"); } @@ -2250,7 +2692,6 @@ convert_code_r(conv_state_t *conv_state, struct icode *ic, struct block *p) struct slist *src; u_int slen; u_int off; - u_int extrajmps; /* number of extra jumps inserted */ struct slist **offset = NULL; if (p == 0 || isMarked(ic, p)) @@ -2374,21 +2815,17 @@ filled: dst->code = (u_short)p->s.code; dst->k = p->s.k; if (JT(p)) { - extrajmps = 0; + /* number of extra jumps inserted */ + u_char extrajmps = 0; off = JT(p)->offset - (p->offset + slen) - 1; if (off >= 256) { /* offset too large for branch, must add a jump */ if (p->longjt == 0) { - /* mark this instruction and retry */ + /* mark this instruction and retry */ p->longjt++; return(0); } - /* branch if T to following jump */ - if (extrajmps >= 256) { - conv_error(conv_state, "too many extra jumps"); - /*NOTREACHED*/ - } - dst->jt = (u_char)extrajmps; + dst->jt = extrajmps; extrajmps++; dst[extrajmps].code = BPF_JMP|BPF_JA; dst[extrajmps].k = off - extrajmps; @@ -2399,17 +2836,13 @@ filled: if (off >= 256) { /* offset too large for branch, must add a jump */ if (p->longjf == 0) { - /* mark this instruction and retry */ + /* mark this instruction and retry */ p->longjf++; return(0); } /* branch if F to following jump */ /* if two jumps are inserted, F goes to second one */ - if (extrajmps >= 256) { - conv_error(conv_state, "too many extra jumps"); - /*NOTREACHED*/ - } - dst->jf = (u_char)extrajmps; + dst->jf = extrajmps; extrajmps++; dst[extrajmps].code = BPF_JMP|BPF_JA; dst[extrajmps].k = off - extrajmps; @@ -2440,7 +2873,7 @@ filled: * done with the filter program. See the pcap man page. */ struct bpf_insn * -icode_to_fcode(struct icode *ic, struct block *root, u_int *lenp, +icode_to_fcode(struct icode *ic, struct block *root, u_int *lenp, char *errbuf) { u_int n; @@ -2464,9 +2897,8 @@ icode_to_fcode(struct icode *ic, struct block *root, u_int *lenp, fp = (struct bpf_insn *)malloc(sizeof(*fp) * n); if (fp == NULL) { - (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, "malloc"); - free(fp); return NULL; } memset((char *)fp, 0, sizeof(*fp) * n); @@ -2491,11 +2923,14 @@ conv_error(conv_state_t *conv_state, const char *fmt, ...) va_list ap; va_start(ap, fmt); - (void)pcap_vsnprintf(conv_state->errbuf, + (void)vsnprintf(conv_state->errbuf, PCAP_ERRBUF_SIZE, fmt, ap); va_end(ap); longjmp(conv_state->top_ctx, 1); /* NOTREACHED */ +#ifdef _AIX + PCAP_UNREACHABLE +#endif /* _AIX */ } /* @@ -2514,8 +2949,8 @@ install_bpf_program(pcap_t *p, struct bpf_program *fp) /* * Validate the program. */ - if (!bpf_validate(fp->bf_insns, fp->bf_len)) { - pcap_snprintf(p->errbuf, sizeof(p->errbuf), + if (!pcap_validate_filter(fp->bf_insns, fp->bf_len)) { + snprintf(p->errbuf, sizeof(p->errbuf), "BPF program is not valid"); return (-1); } @@ -2552,7 +2987,7 @@ dot_dump_node(struct icode *ic, struct block *block, struct bpf_program *prog, icount = slength(block->stmts) + 1 + block->longjt + block->longjf; noffset = min(block->offset + icount, (int)prog->bf_len); - fprintf(out, "\tblock%d [shape=ellipse, id=\"block-%d\" label=\"BLOCK%d\\n", block->id, block->id, block->id); + fprintf(out, "\tblock%u [shape=ellipse, id=\"block-%u\" label=\"BLOCK%u\\n", block->id, block->id, block->id); for (i = block->offset; i < noffset; i++) { fprintf(out, "\\n%s", bpf_image(prog->bf_insns + i, i)); } @@ -2579,9 +3014,9 @@ dot_dump_edge(struct icode *ic, struct block *block, FILE *out) Mark(ic, block); if (JT(block)) { - fprintf(out, "\t\"block%d\":se -> \"block%d\":n [label=\"T\"]; \n", + fprintf(out, "\t\"block%u\":se -> \"block%u\":n [label=\"T\"]; \n", block->id, JT(block)->id); - fprintf(out, "\t\"block%d\":sw -> \"block%d\":n [label=\"F\"]; \n", + fprintf(out, "\t\"block%u\":sw -> \"block%u\":n [label=\"F\"]; \n", block->id, JF(block)->id); } dot_dump_edge(ic, JT(block), out); @@ -2604,7 +3039,7 @@ dot_dump_edge(struct icode *ic, struct block *block, FILE *out) "block1":sw -> "block3":n [label="F"]; } * - * After install graphviz on http://www.graphviz.org/, save it as bpf.dot + * After install graphviz on https://www.graphviz.org/, save it as bpf.dot * and run `dot -Tpng -O bpf.dot' to draw the graph. */ static int diff --git a/org.tcpdump.chmod_bpf.plist b/org.tcpdump.chmod_bpf.plist index 8ad685261a3f..f6fdfec77fa8 100644 --- a/org.tcpdump.chmod_bpf.plist +++ b/org.tcpdump.chmod_bpf.plist @@ -1,5 +1,5 @@ - + Label diff --git a/pcap-airpcap.c b/pcap-airpcap.c new file mode 100644 index 000000000000..510e4c4e6a27 --- /dev/null +++ b/pcap-airpcap.c @@ -0,0 +1,1054 @@ +/* + * Copyright (c) 1999 - 2005 NetGroup, Politecnico di Torino (Italy) + * Copyright (c) 2005 - 2010 CACE Technologies, Davis (California) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Politecnico di Torino, CACE Technologies + * nor the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "pcap-int.h" + +#include + +#include "pcap-airpcap.h" + +/* Default size of the buffer we allocate in userland. */ +#define AIRPCAP_DEFAULT_USER_BUFFER_SIZE 256000 + +/* Default size of the buffer for the AirPcap adapter. */ +#define AIRPCAP_DEFAULT_KERNEL_BUFFER_SIZE 1000000 + +// +// We load the AirPcap DLL dynamically, so that the code will +// work whether you have it installed or not, and there don't +// have to be two different versions of the library, one linked +// to the AirPcap library and one not linked to it. +// +static pcap_code_handle_t airpcap_lib; + +typedef PCHAR (*AirpcapGetLastErrorHandler)(PAirpcapHandle); +typedef BOOL (*AirpcapGetDeviceListHandler)(PAirpcapDeviceDescription *, PCHAR); +typedef VOID (*AirpcapFreeDeviceListHandler)(PAirpcapDeviceDescription); +typedef PAirpcapHandle (*AirpcapOpenHandler)(PCHAR, PCHAR); +typedef VOID (*AirpcapCloseHandler)(PAirpcapHandle); +typedef BOOL (*AirpcapSetDeviceMacFlagsHandler)(PAirpcapHandle, UINT); +typedef BOOL (*AirpcapSetLinkTypeHandler)(PAirpcapHandle, AirpcapLinkType); +typedef BOOL (*AirpcapGetLinkTypeHandler)(PAirpcapHandle, PAirpcapLinkType); +typedef BOOL (*AirpcapSetKernelBufferHandler)(PAirpcapHandle, UINT); +typedef BOOL (*AirpcapSetFilterHandler)(PAirpcapHandle, PVOID, UINT); +typedef BOOL (*AirpcapSetMinToCopyHandler)(PAirpcapHandle, UINT); +typedef BOOL (*AirpcapGetReadEventHandler)(PAirpcapHandle, HANDLE *); +typedef BOOL (*AirpcapReadHandler)(PAirpcapHandle, PBYTE, UINT, PUINT); +typedef BOOL (*AirpcapWriteHandler)(PAirpcapHandle, PCHAR, ULONG); +typedef BOOL (*AirpcapGetStatsHandler)(PAirpcapHandle, PAirpcapStats); + +static AirpcapGetLastErrorHandler p_AirpcapGetLastError; +static AirpcapGetDeviceListHandler p_AirpcapGetDeviceList; +static AirpcapFreeDeviceListHandler p_AirpcapFreeDeviceList; +static AirpcapOpenHandler p_AirpcapOpen; +static AirpcapCloseHandler p_AirpcapClose; +static AirpcapSetDeviceMacFlagsHandler p_AirpcapSetDeviceMacFlags; +static AirpcapSetLinkTypeHandler p_AirpcapSetLinkType; +static AirpcapGetLinkTypeHandler p_AirpcapGetLinkType; +static AirpcapSetKernelBufferHandler p_AirpcapSetKernelBuffer; +static AirpcapSetFilterHandler p_AirpcapSetFilter; +static AirpcapSetMinToCopyHandler p_AirpcapSetMinToCopy; +static AirpcapGetReadEventHandler p_AirpcapGetReadEvent; +static AirpcapReadHandler p_AirpcapRead; +static AirpcapWriteHandler p_AirpcapWrite; +static AirpcapGetStatsHandler p_AirpcapGetStats; + +typedef enum LONG +{ + AIRPCAP_API_UNLOADED = 0, + AIRPCAP_API_LOADED, + AIRPCAP_API_CANNOT_LOAD, + AIRPCAP_API_LOADING +} AIRPCAP_API_LOAD_STATUS; + +static AIRPCAP_API_LOAD_STATUS airpcap_load_status; + +/* + * NOTE: this function should be called by the pcap functions that can + * theoretically deal with the AirPcap library for the first time, + * namely listing the adapters and creating a pcap_t for an adapter. + * All the other ones (activate, close, read, write, set parameters) + * work on a pcap_t for an AirPcap device, meaning we've already + * created the pcap_t and thus have loaded the functions, so we do + * not need to call this function. + */ +static AIRPCAP_API_LOAD_STATUS +load_airpcap_functions(void) +{ + AIRPCAP_API_LOAD_STATUS current_status; + + /* + * We don't use a mutex because there's no place that + * we can guarantee we'll be called before any threads + * other than the main thread exists. (For example, + * this might be a static library, so we can't arrange + * to be called by DllMain(), and there's no guarantee + * that the application called pcap_init() - which is + * supposed to be called only from one thread - so + * we can't arrange to be called from it.) + * + * If nobody's tried to load it yet, mark it as + * loading; in any case, return the status before + * we modified it. + */ + current_status = InterlockedCompareExchange((LONG *)&airpcap_load_status, + AIRPCAP_API_LOADING, AIRPCAP_API_UNLOADED); + + /* + * If the status was AIRPCAP_API_UNLOADED, we've set it + * to AIRPCAP_API_LOADING, because we're going to be + * the ones to load the library but current_status is + * AIRPCAP_API_UNLOADED. + * + * if it was AIRPCAP_API_LOADING, meaning somebody else + * was trying to load it, spin until they finish and + * set the status to a value reflecting whether they + * succeeded. + */ + while (current_status == AIRPCAP_API_LOADING) { + current_status = InterlockedCompareExchange((LONG*)&airpcap_load_status, + AIRPCAP_API_LOADING, AIRPCAP_API_LOADING); + Sleep(10); + } + + /* + * At this point, current_status is either: + * + * AIRPCAP_API_LOADED, in which case another thread + * loaded the library, so we're done; + * + * AIRPCAP_API_CANNOT_LOAD, in which another thread + * tried and failed to load the library, so we're + * done - we won't try it ourselves; + * + * AIRPCAP_API_LOADING, in which case *we're* the + * ones loading it, and should now try to do so. + */ + if (current_status == AIRPCAP_API_LOADED) + return AIRPCAP_API_LOADED; + + if (current_status == AIRPCAP_API_CANNOT_LOAD) + return AIRPCAP_API_CANNOT_LOAD; + + /* + * Start out assuming we can't load it. + */ + current_status = AIRPCAP_API_CANNOT_LOAD; + + airpcap_lib = pcap_load_code("airpcap.dll"); + if (airpcap_lib != NULL) { + /* + * OK, we've loaded the library; now try to find the + * functions we need in it. + */ + p_AirpcapGetLastError = (AirpcapGetLastErrorHandler) pcap_find_function(airpcap_lib, "AirpcapGetLastError"); + p_AirpcapGetDeviceList = (AirpcapGetDeviceListHandler) pcap_find_function(airpcap_lib, "AirpcapGetDeviceList"); + p_AirpcapFreeDeviceList = (AirpcapFreeDeviceListHandler) pcap_find_function(airpcap_lib, "AirpcapFreeDeviceList"); + p_AirpcapOpen = (AirpcapOpenHandler) pcap_find_function(airpcap_lib, "AirpcapOpen"); + p_AirpcapClose = (AirpcapCloseHandler) pcap_find_function(airpcap_lib, "AirpcapClose"); + p_AirpcapSetDeviceMacFlags = (AirpcapSetDeviceMacFlagsHandler) pcap_find_function(airpcap_lib, "AirpcapSetDeviceMacFlags"); + p_AirpcapSetLinkType = (AirpcapSetLinkTypeHandler) pcap_find_function(airpcap_lib, "AirpcapSetLinkType"); + p_AirpcapGetLinkType = (AirpcapGetLinkTypeHandler) pcap_find_function(airpcap_lib, "AirpcapGetLinkType"); + p_AirpcapSetKernelBuffer = (AirpcapSetKernelBufferHandler) pcap_find_function(airpcap_lib, "AirpcapSetKernelBuffer"); + p_AirpcapSetFilter = (AirpcapSetFilterHandler) pcap_find_function(airpcap_lib, "AirpcapSetFilter"); + p_AirpcapSetMinToCopy = (AirpcapSetMinToCopyHandler) pcap_find_function(airpcap_lib, "AirpcapSetMinToCopy"); + p_AirpcapGetReadEvent = (AirpcapGetReadEventHandler) pcap_find_function(airpcap_lib, "AirpcapGetReadEvent"); + p_AirpcapRead = (AirpcapReadHandler) pcap_find_function(airpcap_lib, "AirpcapRead"); + p_AirpcapWrite = (AirpcapWriteHandler) pcap_find_function(airpcap_lib, "AirpcapWrite"); + p_AirpcapGetStats = (AirpcapGetStatsHandler) pcap_find_function(airpcap_lib, "AirpcapGetStats"); + + // + // Make sure that we found everything + // + if (p_AirpcapGetLastError != NULL && + p_AirpcapGetDeviceList != NULL && + p_AirpcapFreeDeviceList != NULL && + p_AirpcapOpen != NULL && + p_AirpcapClose != NULL && + p_AirpcapSetDeviceMacFlags != NULL && + p_AirpcapSetLinkType != NULL && + p_AirpcapGetLinkType != NULL && + p_AirpcapSetKernelBuffer != NULL && + p_AirpcapSetFilter != NULL && + p_AirpcapSetMinToCopy != NULL && + p_AirpcapGetReadEvent != NULL && + p_AirpcapRead != NULL && + p_AirpcapWrite != NULL && + p_AirpcapGetStats != NULL) { + /* + * We have all we need. + */ + current_status = AIRPCAP_API_LOADED; + } + } + + if (current_status != AIRPCAP_API_LOADED) { + /* + * We failed; if we found the DLL, close the + * handle for it. + */ + if (airpcap_lib != NULL) { + FreeLibrary(airpcap_lib); + airpcap_lib = NULL; + } + } + + /* + * Now set the status appropriately - and atomically. + */ + InterlockedExchange((LONG *)&airpcap_load_status, current_status); + + return current_status; +} + +/* + * Private data for capturing on AirPcap devices. + */ +struct pcap_airpcap { + PAirpcapHandle adapter; + int filtering_in_kernel; + int nonblock; + int read_timeout; + HANDLE read_event; + struct pcap_stat stat; +}; + +static int +airpcap_setfilter(pcap_t *p, struct bpf_program *fp) +{ + struct pcap_airpcap *pa = p->priv; + + if (!p_AirpcapSetFilter(pa->adapter, fp->bf_insns, + fp->bf_len * sizeof(struct bpf_insn))) { + /* + * Kernel filter not installed. + * + * XXX - we don't know whether this failed because: + * + * the kernel rejected the filter program as invalid, + * in which case we should fall back on userland + * filtering; + * + * the kernel rejected the filter program as too big, + * in which case we should again fall back on + * userland filtering; + * + * there was some other problem, in which case we + * should probably report an error; + * + * So we just fall back on userland filtering in + * all cases. + */ + + /* + * install_bpf_program() validates the program. + * + * XXX - what if we already have a filter in the kernel? + */ + if (install_bpf_program(p, fp) < 0) + return (-1); + pa->filtering_in_kernel = 0; /* filtering in userland */ + return (0); + } + + /* + * It worked. + */ + pa->filtering_in_kernel = 1; /* filtering in the kernel */ + + /* + * Discard any previously-received packets, as they might have + * passed whatever filter was formerly in effect, but might + * not pass this filter (BIOCSETF discards packets buffered + * in the kernel, so you can lose packets in any case). + */ + p->cc = 0; + return (0); +} + +static int +airpcap_set_datalink(pcap_t *p, int dlt) +{ + struct pcap_airpcap *pa = p->priv; + AirpcapLinkType type; + + switch (dlt) { + + case DLT_IEEE802_11_RADIO: + type = AIRPCAP_LT_802_11_PLUS_RADIO; + break; + + case DLT_PPI: + type = AIRPCAP_LT_802_11_PLUS_PPI; + break; + + case DLT_IEEE802_11: + type = AIRPCAP_LT_802_11; + break; + + default: + /* This can't happen; just return. */ + return (0); + } + if (!p_AirpcapSetLinkType(pa->adapter, type)) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "AirpcapSetLinkType() failed: %s", + p_AirpcapGetLastError(pa->adapter)); + return (-1); + } + p->linktype = dlt; + return (0); +} + +static int +airpcap_getnonblock(pcap_t *p) +{ + struct pcap_airpcap *pa = p->priv; + + return (pa->nonblock); +} + +static int +airpcap_setnonblock(pcap_t *p, int nonblock) +{ + struct pcap_airpcap *pa = p->priv; + int newtimeout; + + if (nonblock) { + /* + * Set the packet buffer timeout to -1 for non-blocking + * mode. + */ + newtimeout = -1; + } else { + /* + * Restore the timeout set when the device was opened. + * (Note that this may be -1, in which case we're not + * really leaving non-blocking mode. However, although + * the timeout argument to pcap_set_timeout() and + * pcap_open_live() is an int, you're not supposed to + * supply a negative value, so that "shouldn't happen".) + */ + newtimeout = p->opt.timeout; + } + pa->read_timeout = newtimeout; + pa->nonblock = (newtimeout == -1); + return (0); +} + +static int +airpcap_stats(pcap_t *p, struct pcap_stat *ps) +{ + struct pcap_airpcap *pa = p->priv; + AirpcapStats tas; + + /* + * Try to get statistics. + */ + if (!p_AirpcapGetStats(pa->adapter, &tas)) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "AirpcapGetStats() failed: %s", + p_AirpcapGetLastError(pa->adapter)); + return (-1); + } + + ps->ps_drop = tas.Drops; + ps->ps_recv = tas.Recvs; + ps->ps_ifdrop = tas.IfDrops; + + return (0); +} + +/* + * Win32-only routine for getting statistics. + * + * This way is definitely safer than passing the pcap_stat * from the userland. + * In fact, there could happen than the user allocates a variable which is not + * big enough for the new structure, and the library will write in a zone + * which is not allocated to this variable. + * + * In this way, we're pretty sure we are writing on memory allocated to this + * variable. + * + * XXX - but this is the wrong way to handle statistics. Instead, we should + * have an API that returns data in a form like the Options section of a + * pcapng Interface Statistics Block: + * + * https://xml2rfc.tools.ietf.org/cgi-bin/xml2rfc.cgi?url=https://raw.githubusercontent.com/pcapng/pcapng/master/draft-tuexen-opsawg-pcapng.xml&modeAsFormat=html/ascii&type=ascii#rfc.section.4.6 + * + * which would let us add new statistics straightforwardly and indicate which + * statistics we are and are *not* providing, rather than having to provide + * possibly-bogus values for statistics we can't provide. + */ +static struct pcap_stat * +airpcap_stats_ex(pcap_t *p, int *pcap_stat_size) +{ + struct pcap_airpcap *pa = p->priv; + AirpcapStats tas; + + *pcap_stat_size = sizeof (p->stat); + + /* + * Try to get statistics. + */ + if (!p_AirpcapGetStats(pa->adapter, &tas)) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "AirpcapGetStats() failed: %s", + p_AirpcapGetLastError(pa->adapter)); + return (NULL); + } + + p->stat.ps_recv = tas.Recvs; + p->stat.ps_drop = tas.Drops; + p->stat.ps_ifdrop = tas.IfDrops; + /* + * Just in case this is ever compiled for a target other than + * Windows, which is extremely unlikely at best. + */ +#ifdef _WIN32 + p->stat.ps_capt = tas.Capt; +#endif + return (&p->stat); +} + +/* Set the dimension of the kernel-level capture buffer */ +static int +airpcap_setbuff(pcap_t *p, int dim) +{ + struct pcap_airpcap *pa = p->priv; + + if (!p_AirpcapSetKernelBuffer(pa->adapter, dim)) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "AirpcapSetKernelBuffer() failed: %s", + p_AirpcapGetLastError(pa->adapter)); + return (-1); + } + return (0); +} + +/* Set the driver working mode */ +static int +airpcap_setmode(pcap_t *p, int mode) +{ + if (mode != MODE_CAPT) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Only MODE_CAPT is supported on an AirPcap adapter"); + return (-1); + } + return (0); +} + +/*set the minimum amount of data that will release a read call*/ +static int +airpcap_setmintocopy(pcap_t *p, int size) +{ + struct pcap_airpcap *pa = p->priv; + + if (!p_AirpcapSetMinToCopy(pa->adapter, size)) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "AirpcapSetMinToCopy() failed: %s", + p_AirpcapGetLastError(pa->adapter)); + return (-1); + } + return (0); +} + +static HANDLE +airpcap_getevent(pcap_t *p) +{ + struct pcap_airpcap *pa = p->priv; + + return (pa->read_event); +} + +static int +airpcap_oid_get_request(pcap_t *p, bpf_u_int32 oid _U_, void *data _U_, + size_t *lenp _U_) +{ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Getting OID values is not supported on an AirPcap adapter"); + return (PCAP_ERROR); +} + +static int +airpcap_oid_set_request(pcap_t *p, bpf_u_int32 oid _U_, const void *data _U_, + size_t *lenp _U_) +{ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Setting OID values is not supported on an AirPcap adapter"); + return (PCAP_ERROR); +} + +static u_int +airpcap_sendqueue_transmit(pcap_t *p, pcap_send_queue *queue _U_, int sync _U_) +{ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Cannot queue packets for transmission on an AirPcap adapter"); + return (0); +} + +static int +airpcap_setuserbuffer(pcap_t *p, int size) +{ + unsigned char *new_buff; + + if (size <= 0) { + /* Bogus parameter */ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Error: invalid size %d",size); + return (-1); + } + + /* Allocate the buffer */ + new_buff = (unsigned char *)malloc(sizeof(char)*size); + + if (!new_buff) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Error: not enough memory"); + return (-1); + } + + free(p->buffer); + + p->buffer = new_buff; + p->bufsize = size; + + return (0); +} + +static int +airpcap_live_dump(pcap_t *p, char *filename _U_, int maxsize _U_, + int maxpacks _U_) +{ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "AirPcap adapters don't support live dump"); + return (-1); +} + +static int +airpcap_live_dump_ended(pcap_t *p, int sync _U_) +{ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "AirPcap adapters don't support live dump"); + return (-1); +} + +static PAirpcapHandle +airpcap_get_airpcap_handle(pcap_t *p) +{ + struct pcap_airpcap *pa = p->priv; + + return (pa->adapter); +} + +static int +airpcap_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) +{ + struct pcap_airpcap *pa = p->priv; + int cc; + int n; + register u_char *bp, *ep; + UINT bytes_read; + u_char *datap; + + cc = p->cc; + if (cc == 0) { + /* + * Has "pcap_breakloop()" been called? + */ + if (p->break_loop) { + /* + * Yes - clear the flag that indicates that it + * has, and return PCAP_ERROR_BREAK to indicate + * that we were told to break out of the loop. + */ + p->break_loop = 0; + return (PCAP_ERROR_BREAK); + } + + // + // If we're not in non-blocking mode, wait for data to + // arrive. + // + if (pa->read_timeout != -1) { + WaitForSingleObject(pa->read_event, + (pa->read_timeout ==0 )? INFINITE: pa->read_timeout); + } + + // + // Read the data. + // p_AirpcapRead doesn't block. + // + if (!p_AirpcapRead(pa->adapter, (PBYTE)p->buffer, + p->bufsize, &bytes_read)) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "AirpcapRead() failed: %s", + p_AirpcapGetLastError(pa->adapter)); + return (-1); + } + cc = bytes_read; + bp = (u_char *)p->buffer; + } else + bp = p->bp; + + /* + * Loop through each packet. + * + * This assumes that a single buffer of packets will have + * <= INT_MAX packets, so the packet count doesn't overflow. + */ +#define bhp ((AirpcapBpfHeader *)bp) + n = 0; + ep = bp + cc; + for (;;) { + register u_int caplen, hdrlen; + + /* + * Has "pcap_breakloop()" been called? + * If so, return immediately - if we haven't read any + * packets, clear the flag and return PCAP_ERROR_BREAK + * to indicate that we were told to break out of the loop, + * otherwise leave the flag set, so that the *next* call + * will break out of the loop without having read any + * packets, and return the number of packets we've + * processed so far. + */ + if (p->break_loop) { + if (n == 0) { + p->break_loop = 0; + return (PCAP_ERROR_BREAK); + } else { + p->bp = bp; + p->cc = (int) (ep - bp); + return (n); + } + } + if (bp >= ep) + break; + + caplen = bhp->Caplen; + hdrlen = bhp->Hdrlen; + datap = bp + hdrlen; + /* + * Short-circuit evaluation: if using BPF filter + * in the AirPcap adapter, no need to do it now - + * we already know the packet passed the filter. + */ + if (pa->filtering_in_kernel || + p->fcode.bf_insns == NULL || + pcap_filter(p->fcode.bf_insns, datap, bhp->Originallen, caplen)) { + struct pcap_pkthdr pkthdr; + + pkthdr.ts.tv_sec = bhp->TsSec; + pkthdr.ts.tv_usec = bhp->TsUsec; + pkthdr.caplen = caplen; + pkthdr.len = bhp->Originallen; + (*callback)(user, &pkthdr, datap); + bp += AIRPCAP_WORDALIGN(caplen + hdrlen); + if (++n >= cnt && !PACKET_COUNT_IS_UNLIMITED(cnt)) { + p->bp = bp; + p->cc = (int)(ep - bp); + return (n); + } + } else { + /* + * Skip this packet. + */ + bp += AIRPCAP_WORDALIGN(caplen + hdrlen); + } + } +#undef bhp + p->cc = 0; + return (n); +} + +static int +airpcap_inject(pcap_t *p, const void *buf, int size) +{ + struct pcap_airpcap *pa = p->priv; + + /* + * XXX - the second argument to AirpcapWrite() *should* have + * been declared as a const pointer - a write function that + * stomps on what it writes is *extremely* rude - but such + * is life. We assume it is, in fact, not going to write on + * our buffer. + */ + if (!p_AirpcapWrite(pa->adapter, (void *)buf, size)) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "AirpcapWrite() failed: %s", + p_AirpcapGetLastError(pa->adapter)); + return (-1); + } + + /* + * We assume it all got sent if "AirpcapWrite()" succeeded. + * "pcap_inject()" is expected to return the number of bytes + * sent. + */ + return (size); +} + +static void +airpcap_cleanup(pcap_t *p) +{ + struct pcap_airpcap *pa = p->priv; + + if (pa->adapter != NULL) { + p_AirpcapClose(pa->adapter); + pa->adapter = NULL; + } + pcap_cleanup_live_common(p); +} + +static void +airpcap_breakloop(pcap_t *p) +{ + HANDLE read_event; + + pcap_breakloop_common(p); + struct pcap_airpcap *pa = p->priv; + + /* XXX - what if either of these fail? */ + /* + * XXX - will SetEvent() force a wakeup and, if so, will + * the AirPcap read code handle that sanely? + */ + if (!p_AirpcapGetReadEvent(pa->adapter, &read_event)) + return; + SetEvent(read_event); +} + +static int +airpcap_activate(pcap_t *p) +{ + struct pcap_airpcap *pa = p->priv; + char *device = p->opt.device; + char airpcap_errbuf[AIRPCAP_ERRBUF_SIZE]; + BOOL status; + AirpcapLinkType link_type; + + pa->adapter = p_AirpcapOpen(device, airpcap_errbuf); + if (pa->adapter == NULL) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s", airpcap_errbuf); + return (PCAP_ERROR); + } + + /* + * Set monitor mode appropriately. + * Always turn off the "ACK frames sent to the card" mode. + */ + if (p->opt.rfmon) { + status = p_AirpcapSetDeviceMacFlags(pa->adapter, + AIRPCAP_MF_MONITOR_MODE_ON); + } else + status = p_AirpcapSetDeviceMacFlags(pa->adapter, + AIRPCAP_MF_ACK_FRAMES_ON); + if (!status) { + p_AirpcapClose(pa->adapter); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "AirpcapSetDeviceMacFlags() failed: %s", + p_AirpcapGetLastError(pa->adapter)); + return (PCAP_ERROR); + } + + /* + * Turn a negative snapshot value (invalid), a snapshot value of + * 0 (unspecified), or a value bigger than the normal maximum + * value, into the maximum allowed value. + * + * If some application really *needs* a bigger snapshot + * length, we should just increase MAXIMUM_SNAPLEN. + */ + if (p->snapshot <= 0 || p->snapshot > MAXIMUM_SNAPLEN) + p->snapshot = MAXIMUM_SNAPLEN; + + /* + * If the buffer size wasn't explicitly set, default to + * AIRPCAP_DEFAULT_KERNEL_BUFFER_SIZE. + */ + if (p->opt.buffer_size == 0) + p->opt.buffer_size = AIRPCAP_DEFAULT_KERNEL_BUFFER_SIZE; + + if (!p_AirpcapSetKernelBuffer(pa->adapter, p->opt.buffer_size)) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "AirpcapSetKernelBuffer() failed: %s", + p_AirpcapGetLastError(pa->adapter)); + goto bad; + } + + if(!p_AirpcapGetReadEvent(pa->adapter, &pa->read_event)) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "AirpcapGetReadEvent() failed: %s", + p_AirpcapGetLastError(pa->adapter)); + goto bad; + } + + /* Set the buffer size */ + p->bufsize = AIRPCAP_DEFAULT_USER_BUFFER_SIZE; + p->buffer = malloc(p->bufsize); + if (p->buffer == NULL) { + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); + goto bad; + } + + if (p->opt.immediate) { + /* Tell the driver to copy the buffer as soon as data arrives. */ + if (!p_AirpcapSetMinToCopy(pa->adapter, 0)) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "AirpcapSetMinToCopy() failed: %s", + p_AirpcapGetLastError(pa->adapter)); + goto bad; + } + } else { + /* + * Tell the driver to copy the buffer only if it contains + * at least 16K. + */ + if (!p_AirpcapSetMinToCopy(pa->adapter, 16000)) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "AirpcapSetMinToCopy() failed: %s", + p_AirpcapGetLastError(pa->adapter)); + goto bad; + } + } + + /* + * Find out what the default link-layer header type is, + * and set p->datalink to that. + * + * We don't force it to another value because there + * might be some programs using WinPcap/Npcap that, + * when capturing on AirPcap devices, assume the + * default value set with the AirPcap configuration + * program is what you get. + * + * The out-of-the-box default appears to be radiotap. + */ + if (!p_AirpcapGetLinkType(pa->adapter, &link_type)) { + /* That failed. */ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "AirpcapGetLinkType() failed: %s", + p_AirpcapGetLastError(pa->adapter)); + goto bad; + } + switch (link_type) { + + case AIRPCAP_LT_802_11_PLUS_RADIO: + p->linktype = DLT_IEEE802_11_RADIO; + break; + + case AIRPCAP_LT_802_11_PLUS_PPI: + p->linktype = DLT_PPI; + break; + + case AIRPCAP_LT_802_11: + p->linktype = DLT_IEEE802_11; + break; + + case AIRPCAP_LT_UNKNOWN: + default: + /* OK, what? */ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "AirpcapGetLinkType() returned unknown link type %u", + link_type); + goto bad; + } + + /* + * Now provide a list of all the supported types; we + * assume they all work. We put radiotap at the top, + * followed by PPI, followed by "no radio metadata". + */ + p->dlt_list = (u_int *) malloc(sizeof(u_int) * 3); + if (p->dlt_list == NULL) + goto bad; + p->dlt_list[0] = DLT_IEEE802_11_RADIO; + p->dlt_list[1] = DLT_PPI; + p->dlt_list[2] = DLT_IEEE802_11; + p->dlt_count = 3; + + p->read_op = airpcap_read; + p->inject_op = airpcap_inject; + p->setfilter_op = airpcap_setfilter; + p->setdirection_op = NULL; /* Not implemented. */ + p->set_datalink_op = airpcap_set_datalink; + p->getnonblock_op = airpcap_getnonblock; + p->setnonblock_op = airpcap_setnonblock; + p->breakloop_op = airpcap_breakloop; + p->stats_op = airpcap_stats; + p->stats_ex_op = airpcap_stats_ex; + p->setbuff_op = airpcap_setbuff; + p->setmode_op = airpcap_setmode; + p->setmintocopy_op = airpcap_setmintocopy; + p->getevent_op = airpcap_getevent; + p->oid_get_request_op = airpcap_oid_get_request; + p->oid_set_request_op = airpcap_oid_set_request; + p->sendqueue_transmit_op = airpcap_sendqueue_transmit; + p->setuserbuffer_op = airpcap_setuserbuffer; + p->live_dump_op = airpcap_live_dump; + p->live_dump_ended_op = airpcap_live_dump_ended; + p->get_airpcap_handle_op = airpcap_get_airpcap_handle; + p->cleanup_op = airpcap_cleanup; + + return (0); + bad: + airpcap_cleanup(p); + return (PCAP_ERROR); +} + +/* + * Monitor mode is supported. + */ +static int +airpcap_can_set_rfmon(pcap_t *p) +{ + return (1); +} + +int +device_is_airpcap(const char *device, char *ebuf) +{ + static const char airpcap_prefix[] = "\\\\.\\airpcap"; + + /* + * We don't determine this by calling AirpcapGetDeviceList() + * and looking at the list, as that appears to be a costly + * operation. + * + * Instead, we just check whether it begins with "\\.\airpcap". + */ + if (strncmp(device, airpcap_prefix, sizeof airpcap_prefix - 1) == 0) { + /* + * Yes, it's an AirPcap device. + */ + return (1); + } + + /* + * No, it's not an AirPcap device. + */ + return (0); +} + +pcap_t * +airpcap_create(const char *device, char *ebuf, int *is_ours) +{ + int ret; + pcap_t *p; + + /* + * This can be called before we've tried loading the library, + * so do so if we haven't already tried to do so. + */ + if (load_airpcap_functions() != AIRPCAP_API_LOADED) { + /* + * We assume this means that we don't have the AirPcap + * software installed, which probably means we don't + * have an AirPcap device. + * + * Don't treat that as an error. + */ + *is_ours = 0; + return (NULL); + } + + /* + * Is this an AirPcap device? + */ + ret = device_is_airpcap(device, ebuf); + if (ret == 0) { + /* No. */ + *is_ours = 0; + return (NULL); + } + + /* + * Yes. + */ + *is_ours = 1; + p = PCAP_CREATE_COMMON(ebuf, struct pcap_airpcap); + if (p == NULL) + return (NULL); + + p->activate_op = airpcap_activate; + p->can_set_rfmon_op = airpcap_can_set_rfmon; + return (p); +} + +/* + * Add all AirPcap devices. + */ +int +airpcap_findalldevs(pcap_if_list_t *devlistp, char *errbuf) +{ + AirpcapDeviceDescription *airpcap_devices, *airpcap_device; + char airpcap_errbuf[AIRPCAP_ERRBUF_SIZE]; + + /* + * This can be called before we've tried loading the library, + * so do so if we haven't already tried to do so. + */ + if (load_airpcap_functions() != AIRPCAP_API_LOADED) { + /* + * XXX - unless the error is "no such DLL", report this + * as an error rather than as "no AirPcap devices"? + */ + return (0); + } + + if (!p_AirpcapGetDeviceList(&airpcap_devices, airpcap_errbuf)) { + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "AirpcapGetDeviceList() failed: %s", airpcap_errbuf); + return (-1); + } + + for (airpcap_device = airpcap_devices; airpcap_device != NULL; + airpcap_device = airpcap_device->next) { + if (add_dev(devlistp, airpcap_device->Name, 0, + airpcap_device->Description, errbuf) == NULL) { + /* + * Failure. + */ + p_AirpcapFreeDeviceList(airpcap_devices); + return (-1); + } + } + p_AirpcapFreeDeviceList(airpcap_devices); + return (0); +} diff --git a/pcap-airpcap.h b/pcap-airpcap.h new file mode 100644 index 000000000000..aa1164ed4c89 --- /dev/null +++ b/pcap-airpcap.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 1999 - 2005 NetGroup, Politecnico di Torino (Italy) + * Copyright (c) 2005 - 2010 CACE Technologies, Davis (California) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Politecnico di Torino, CACE Technologies + * nor the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +pcap_t *airpcap_create(const char *, char *, int *); +int airpcap_findalldevs(pcap_if_list_t *devlistp, char *errbuf); +int device_is_airpcap(const char *device, char *ebuf); diff --git a/pcap-bpf.c b/pcap-bpf.c index 4f1a0afbe69e..2898e598d8e3 100644 --- a/pcap-bpf.c +++ b/pcap-bpf.c @@ -115,7 +115,6 @@ static int bpf_load(char *errbuf); #endif /* _AIX */ -#include #include #include #include @@ -153,7 +152,7 @@ struct pcap_bpf { * As there is a header on the front size of the mmap'd buffer, only * some of the buffer is exposed to libpcap as a whole via bufsize; * zbufsize is the true size. zbuffer tracks the current zbuf - * assocated with buffer so that it can be used to decide which the + * associated with buffer so that it can be used to decide which the * next buffer to read will be. */ u_char *zbuf1, *zbuf2, *zbuffer; @@ -313,7 +312,7 @@ pcap_next_zbuf_shm(pcap_t *p, int *cc) atomic_load_acq_int(&bzh->bzh_kernel_gen)) { pb->bzh = bzh; pb->zbuffer = (u_char *)pb->zbuf2; - p->buffer = pb->zbuffer + sizeof(*bzh); + p->buffer = pb->zbuffer + sizeof(*bzh); *cc = bzh->bzh_kernel_len; return (1); } @@ -445,7 +444,7 @@ pcap_create_interface(const char *device _U_, char *ebuf) { pcap_t *p; - p = pcap_create_common(ebuf, sizeof (struct pcap_bpf)); + p = PCAP_CREATE_COMMON(ebuf, struct pcap_bpf); if (p == NULL) return (NULL); @@ -456,7 +455,6 @@ pcap_create_interface(const char *device _U_, char *ebuf) * We claim that we support microsecond and nanosecond time * stamps. */ - p->tstamp_precision_count = 2; p->tstamp_precision_list = malloc(2 * sizeof(u_int)); if (p->tstamp_precision_list == NULL) { pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, errno, @@ -466,6 +464,7 @@ pcap_create_interface(const char *device _U_, char *ebuf) } p->tstamp_precision_list[0] = PCAP_TSTAMP_PRECISION_MICRO; p->tstamp_precision_list[1] = PCAP_TSTAMP_PRECISION_NANO; + p->tstamp_precision_count = 2; #endif /* BIOCSTSTAMP */ return (p); } @@ -479,7 +478,7 @@ bpf_open(char *errbuf) { int fd = -1; static const char cloning_device[] = "/dev/bpf"; - int n = 0; + u_int n = 0; char device[sizeof "/dev/bpf0000000000"]; static int no_cloning_bpf = 0; @@ -504,12 +503,17 @@ bpf_open(char *errbuf) ((errno != EACCES && errno != ENOENT) || (fd = open(cloning_device, O_RDONLY)) == -1)) { if (errno != ENOENT) { - if (errno == EACCES) + if (errno == EACCES) { fd = PCAP_ERROR_PERM_DENIED; - else + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "Attempt to open %s failed - root privileges may be required", + cloning_device); + } else { fd = PCAP_ERROR; - pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, - errno, "(cannot open device) %s", cloning_device); + pcap_fmt_errmsg_for_errno(errbuf, + PCAP_ERRBUF_SIZE, errno, + "(cannot open device) %s", cloning_device); + } return (fd); } no_cloning_bpf = 1; @@ -522,7 +526,7 @@ bpf_open(char *errbuf) * that isn't in use. */ do { - (void)pcap_snprintf(device, sizeof(device), "/dev/bpf%d", n++); + (void)snprintf(device, sizeof(device), "/dev/bpf%u", n++); /* * Initially try a read/write open (to allow the inject * method to work). If that fails due to permission @@ -557,7 +561,7 @@ bpf_open(char *errbuf) * means we probably have no BPF * devices. */ - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "(there are no BPF devices)"); } else { /* @@ -566,7 +570,7 @@ bpf_open(char *errbuf) * devices, but all the ones * that exist are busy. */ - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "(all BPF devices are busy)"); } break; @@ -578,8 +582,9 @@ bpf_open(char *errbuf) * if any. */ fd = PCAP_ERROR_PERM_DENIED; - pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, - errno, "(cannot open BPF device) %s", device); + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "Attempt to open %s failed - root privileges may be required", + device); break; default: @@ -596,6 +601,106 @@ bpf_open(char *errbuf) return (fd); } +/* + * Bind a network adapter to a BPF device, given a descriptor for the + * BPF device and the name of the network adapter. + * + * Use BIOCSETLIF if available (meaning "on Solaris"), as it supports + * longer device names. + * + * If the name is longer than will fit, return PCAP_ERROR_NO_SUCH_DEVICE + * before trying to bind the interface, as there cannot be such a device. + * + * If the attempt succeeds, return BPF_BIND_SUCCEEDED. + * + * If the attempt fails: + * + * if it fails with ENXIO, return PCAP_ERROR_NO_SUCH_DEVICE, as + * the device doesn't exist; + * + * if it fails with ENETDOWN, return PCAP_ERROR_IFACE_NOT_UP, as + * the interface exists but isn't up and the OS doesn't allow + * binding to an interface that isn't up; + * + * if it fails with ENOBUFS, return BPF_BIND_BUFFER_TOO_BIG, and + * fill in an error message, as the buffer being requested is too + * large; + * + * otherwise, return PCAP_ERROR and fill in an error message. + */ +#define BPF_BIND_SUCCEEDED 0 +#define BPF_BIND_BUFFER_TOO_BIG 1 + +static int +bpf_bind(int fd, const char *name, char *errbuf) +{ + int status; +#ifdef LIFNAMSIZ + struct lifreq ifr; + + if (strlen(name) >= sizeof(ifr.lifr_name)) { + /* The name is too long, so it can't possibly exist. */ + return (PCAP_ERROR_NO_SUCH_DEVICE); + } + (void)pcap_strlcpy(ifr.lifr_name, name, sizeof(ifr.lifr_name)); + status = ioctl(fd, BIOCSETLIF, (caddr_t)&ifr); +#else + struct ifreq ifr; + + if (strlen(name) >= sizeof(ifr.ifr_name)) { + /* The name is too long, so it can't possibly exist. */ + return (PCAP_ERROR_NO_SUCH_DEVICE); + } + (void)pcap_strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + status = ioctl(fd, BIOCSETIF, (caddr_t)&ifr); +#endif + + if (status < 0) { + switch (errno) { + + case ENXIO: + /* + * There's no such device. + * + * There's nothing more to say, so clear out the + * error message. + */ + errbuf[0] = '\0'; + return (PCAP_ERROR_NO_SUCH_DEVICE); + + case ENETDOWN: + /* + * Return a "network down" indication, so that + * the application can report that rather than + * saying we had a mysterious failure and + * suggest that they report a problem to the + * libpcap developers. + */ + return (PCAP_ERROR_IFACE_NOT_UP); + + case ENOBUFS: + /* + * The buffer size is too big. + * Return a special indication so that, if we're + * trying to crank the buffer size down, we know + * we have to continue; add an error message that + * tells the user what needs to be fixed. + */ + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "The requested buffer size for %s is too large", + name); + return (BPF_BIND_BUFFER_TOO_BIG); + + default: + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "Binding interface %s to BPF device failed", + name); + return (PCAP_ERROR); + } + } + return (BPF_BIND_SUCCEEDED); +} + /* * Open and bind to a device; used if we're not actually going to use * the device, but are just testing whether it can be opened, or opening @@ -608,7 +713,7 @@ static int bpf_open_and_bind(const char *name, char *errbuf) { int fd; - struct ifreq ifr; + int status; /* * First, open a BPF device. @@ -620,34 +725,18 @@ bpf_open_and_bind(const char *name, char *errbuf) /* * Now bind to the device. */ - (void)strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); - if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) < 0) { - switch (errno) { - - case ENXIO: + status = bpf_bind(fd, name, errbuf); + if (status != BPF_BIND_SUCCEEDED) { + close(fd); + if (status == BPF_BIND_BUFFER_TOO_BIG) { /* - * There's no such device. + * We didn't specify a buffer size, so + * this *really* shouldn't fail because + * there's no buffer space. Fail. */ - close(fd); - return (PCAP_ERROR_NO_SUCH_DEVICE); - - case ENETDOWN: - /* - * Return a "network down" indication, so that - * the application can report that rather than - * saying we had a mysterious failure and - * suggest that they report a problem to the - * libpcap developers. - */ - close(fd); - return (PCAP_ERROR_IFACE_NOT_UP); - - default: - pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, - errno, "BIOCSETIF: %s", name); - close(fd); return (PCAP_ERROR); } + return (status); } /* @@ -656,6 +745,47 @@ bpf_open_and_bind(const char *name, char *errbuf) return (fd); } +#ifdef __APPLE__ +static int +device_exists(int fd, const char *name, char *errbuf) +{ + int status; + struct ifreq ifr; + + if (strlen(name) >= sizeof(ifr.ifr_name)) { + /* The name is too long, so it can't possibly exist. */ + return (PCAP_ERROR_NO_SUCH_DEVICE); + } + (void)pcap_strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + status = ioctl(fd, SIOCGIFFLAGS, (caddr_t)&ifr); + + if (status < 0) { + if (errno == ENXIO || errno == EINVAL) { + /* + * macOS and *BSD return one of those two + * errors if the device doesn't exist. + * Don't fill in an error, as this is + * an "expected" condition. + */ + return (PCAP_ERROR_NO_SUCH_DEVICE); + } + + /* + * Some other error - provide a message for it, as + * it's "unexpected". + */ + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, + "Can't get interface flags on %s", name); + return (PCAP_ERROR); + } + + /* + * The device exists. + */ + return (0); +} +#endif + #ifdef BIOCGDLTLIST static int get_dlt_list(int fd, int v, struct bpf_dltlist *bdlp, char *ebuf) @@ -742,10 +872,10 @@ static int pcap_can_set_rfmon_bpf(pcap_t *p) { struct utsname osinfo; - struct ifreq ifr; int fd; #ifdef BIOCGDLTLIST struct bpf_dltlist bdl; + int err; #endif /* @@ -783,6 +913,9 @@ pcap_can_set_rfmon_bpf(pcap_t *p) return (0); } if (osinfo.release[0] == '8' && osinfo.release[1] == '.') { + char *wlt_name; + int status; + /* * 10.4 (Darwin 8.x). s/en/wlt/, and check * whether the device exists. @@ -799,16 +932,24 @@ pcap_can_set_rfmon_bpf(pcap_t *p) errno, "socket"); return (PCAP_ERROR); } - pcap_strlcpy(ifr.ifr_name, "wlt", sizeof(ifr.ifr_name)); - pcap_strlcat(ifr.ifr_name, p->opt.device + 2, sizeof(ifr.ifr_name)); - if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifr) < 0) { - /* - * No such device? - */ + if (pcap_asprintf(&wlt_name, "wlt%s", p->opt.device + 2) == -1) { + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); close(fd); - return (0); + return (PCAP_ERROR); } + status = device_exists(fd, wlt_name, p->errbuf); + free(wlt_name); close(fd); + if (status != 0) { + if (status == PCAP_ERROR_NO_SUCH_DEVICE) + return (0); + + /* + * Error. + */ + return (status); + } return (1); } @@ -827,34 +968,18 @@ pcap_can_set_rfmon_bpf(pcap_t *p) /* * Now bind to the device. */ - (void)strncpy(ifr.ifr_name, p->opt.device, sizeof(ifr.ifr_name)); - if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) < 0) { - switch (errno) { - - case ENXIO: + err = bpf_bind(fd, p->opt.device, p->errbuf); + if (err != BPF_BIND_SUCCEEDED) { + close(fd); + if (err == BPF_BIND_BUFFER_TOO_BIG) { /* - * There's no such device. + * We didn't specify a buffer size, so + * this *really* shouldn't fail because + * there's no buffer space. Fail. */ - close(fd); - return (PCAP_ERROR_NO_SUCH_DEVICE); - - case ENETDOWN: - /* - * Return a "network down" indication, so that - * the application can report that rather than - * saying we had a mysterious failure and - * suggest that they report a problem to the - * libpcap developers. - */ - close(fd); - return (PCAP_ERROR_IFACE_NOT_UP); - - default: - pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, - errno, "BIOCSETIF: %s", p->opt.device); - close(fd); return (PCAP_ERROR); } + return (err); } /* @@ -982,7 +1107,7 @@ pcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user) } else #endif { - cc = read(p->fd, p->buffer, p->bufsize); + cc = (int)read(p->fd, p->buffer, p->bufsize); } if (cc < 0) { /* Don't choke when we get ptraced */ @@ -1033,7 +1158,7 @@ pcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user) * documented as having error returns * other than PCAP_ERROR or PCAP_ERROR_BREAK. */ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "The interface disappeared"); return (PCAP_ERROR); @@ -1062,6 +1187,9 @@ pcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user) /* * Loop through each packet. + * + * This assumes that a single buffer of packets will have + * <= INT_MAX packets, so the packet count doesn't overflow. */ #ifdef BIOCSTSTAMP #define bhp ((struct bpf_xhdr *)bp) @@ -1087,7 +1215,7 @@ pcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user) */ if (p->break_loop) { p->bp = bp; - p->cc = ep - bp; + p->cc = (int)(ep - bp); /* * ep is set based on the return value of read(), * but read() from a BPF device doesn't necessarily @@ -1127,7 +1255,7 @@ pcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user) #endif */ if (pb->filtering_in_kernel || - bpf_filter(p->fcode.bf_insns, datap, bhp->bh_datalen, caplen)) { + pcap_filter(p->fcode.bf_insns, datap, bhp->bh_datalen, caplen)) { struct pcap_pkthdr pkthdr; #ifdef BIOCSTSTAMP struct bintime bt; @@ -1177,7 +1305,7 @@ pcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user) bp += BPF_WORDALIGN(caplen + hdrlen); if (++n >= cnt && !PACKET_COUNT_IS_UNLIMITED(cnt)) { p->bp = bp; - p->cc = ep - bp; + p->cc = (int)(ep - bp); /* * See comment above about p->cc < 0. */ @@ -1198,11 +1326,11 @@ pcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user) } static int -pcap_inject_bpf(pcap_t *p, const void *buf, size_t size) +pcap_inject_bpf(pcap_t *p, const void *buf, int size) { int ret; - ret = write(p->fd, buf, size); + ret = (int)write(p->fd, buf, size); #ifdef __APPLE__ if (ret == -1 && errno == EAFNOSUPPORT) { /* @@ -1234,7 +1362,7 @@ pcap_inject_bpf(pcap_t *p, const void *buf, size_t size) /* * Now try the write again. */ - ret = write(p->fd, buf, size); + ret = (int)write(p->fd, buf, size); } #endif /* __APPLE__ */ if (ret == -1) { @@ -1254,7 +1382,7 @@ bpf_odminit(char *errbuf) if (odm_initialize() == -1) { if (odm_err_msg(odmerrno, &errstr) == -1) errstr = "Unknown error"; - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "bpf_load: odm_initialize failed: %s", errstr); return (PCAP_ERROR); @@ -1263,7 +1391,7 @@ bpf_odminit(char *errbuf) if ((odmlockid = odm_lock("/etc/objrepos/config_lock", ODM_WAIT)) == -1) { if (odm_err_msg(odmerrno, &errstr) == -1) errstr = "Unknown error"; - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "bpf_load: odm_lock of /etc/objrepos/config_lock failed: %s", errstr); (void)odm_terminate(); @@ -1282,7 +1410,7 @@ bpf_odmcleanup(char *errbuf) if (errbuf != NULL) { if (odm_err_msg(odmerrno, &errstr) == -1) errstr = "Unknown error"; - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "bpf_load: odm_unlock failed: %s", errstr); } @@ -1293,7 +1421,7 @@ bpf_odmcleanup(char *errbuf) if (errbuf != NULL) { if (odm_err_msg(odmerrno, &errstr) == -1) errstr = "Unknown error"; - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "bpf_load: odm_terminate failed: %s", errstr); } @@ -1356,7 +1484,7 @@ bpf_load(char *errbuf) if (rc == -1 || getmajor(sbuf.st_rdev) != major) { for (i = 0; i < BPF_MINORS; i++) { - pcap_snprintf(buf, sizeof(buf), "%s%d", BPF_NODE, i); + snprintf(buf, sizeof(buf), "%s%d", BPF_NODE, i); unlink(buf); if (mknod(buf, S_IRUSR | S_IFCHR, domakedev(major, i)) == -1) { pcap_fmt_errmsg_for_errno(errbuf, @@ -1369,7 +1497,7 @@ bpf_load(char *errbuf) /* Check if the driver is loaded */ memset(&cfg_ld, 0x0, sizeof(cfg_ld)); - pcap_snprintf(buf, sizeof(buf), "%s/%s", DRIVER_PATH, BPF_NAME); + snprintf(buf, sizeof(buf), "%s/%s", DRIVER_PATH, BPF_NAME); cfg_ld.path = buf; if ((sysconfig(SYS_QUERYLOAD, (void *)&cfg_ld, sizeof(cfg_ld)) == -1) || (cfg_ld.kmid == 0)) { @@ -1437,7 +1565,7 @@ pcap_cleanup_bpf(pcap_t *p) strerror(errno)); } else { memset(&req, 0, sizeof(req)); - strncpy(req.ifm_name, pb->device, + pcap_strlcpy(req.ifm_name, pb->device, sizeof(req.ifm_name)); if (ioctl(sock, SIOCGIFMEDIA, &req) < 0) { fprintf(stderr, @@ -1451,7 +1579,7 @@ pcap_cleanup_bpf(pcap_t *p) * turn it off. */ memset(&ifr, 0, sizeof(ifr)); - (void)strncpy(ifr.ifr_name, + (void)pcap_strlcpy(ifr.ifr_name, pb->device, sizeof(ifr.ifr_name)); ifr.ifr_media = @@ -1519,20 +1647,17 @@ pcap_cleanup_bpf(pcap_t *p) pcap_cleanup_live_common(p); } +#ifdef __APPLE__ static int check_setif_failure(pcap_t *p, int error) { -#ifdef __APPLE__ int fd; - struct ifreq ifr; int err; -#endif - if (error == ENXIO) { + if (error == PCAP_ERROR_NO_SUCH_DEVICE) { /* * No such device exists. */ -#ifdef __APPLE__ if (p->opt.rfmon && strncmp(p->opt.device, "wlt", 3) == 0) { /* * Monitor mode was requested, and we're trying @@ -1543,33 +1668,38 @@ check_setif_failure(pcap_t *p, int error) */ fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd != -1) { - pcap_strlcpy(ifr.ifr_name, "en", - sizeof(ifr.ifr_name)); - pcap_strlcat(ifr.ifr_name, p->opt.device + 3, - sizeof(ifr.ifr_name)); - if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifr) < 0) { + char *en_name; + + if (pcap_asprintf(&en_name, "en%s", + p->opt.device + 3) == -1) { /* - * We assume this failed because - * the underlying device doesn't - * exist. + * We can't find out whether there's + * an underlying "enN" device, so + * just report "no such device". */ - err = PCAP_ERROR_NO_SUCH_DEVICE; pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, errno, - "SIOCGIFFLAGS on %s failed", - ifr.ifr_name); - } else { - /* - * The underlying "enN" device - * exists, but there's no - * corresponding "wltN" device; - * that means that the "enN" - * device doesn't support - * monitor mode, probably because - * it's an Ethernet device rather - * than a wireless device. - */ - err = PCAP_ERROR_RFMON_NOTSUP; + "malloc"); + close(fd); + return (PCAP_ERROR_NO_SUCH_DEVICE); + } + err = device_exists(fd, en_name, p->errbuf); + free(en_name); + if (err != 0) { + if (err == PCAP_ERROR_NO_SUCH_DEVICE) { + /* + * The underlying "enN" device + * exists, but there's no + * corresponding "wltN" device; + * that means that the "enN" + * device doesn't support + * monitor mode, probably + * because it's an Ethernet + * device rather than a + * wireless device. + */ + err = PCAP_ERROR_RFMON_NOTSUP; + } } close(fd); } else { @@ -1585,32 +1715,30 @@ check_setif_failure(pcap_t *p, int error) } return (err); } -#endif + /* * No such device. */ - pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, - errno, "BIOCSETIF failed"); return (PCAP_ERROR_NO_SUCH_DEVICE); - } else if (errno == ENETDOWN) { - /* - * Return a "network down" indication, so that - * the application can report that rather than - * saying we had a mysterious failure and - * suggest that they report a problem to the - * libpcap developers. - */ - return (PCAP_ERROR_IFACE_NOT_UP); - } else { - /* - * Some other error; fill in the error string, and - * return PCAP_ERROR. - */ - pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, - errno, "BIOCSETIF: %s", p->opt.device); - return (PCAP_ERROR); } + + /* + * Just return the error status; it's what we want, and, if it's + * PCAP_ERROR, the error string has been filled in. + */ + return (error); } +#else +static int +check_setif_failure(pcap_t *p _U_, int error) +{ + /* + * Just return the error status; it's what we want, and, if it's + * PCAP_ERROR, the error string has been filled in. + */ + return (error); +} +#endif /* * Default capture buffer size. @@ -1636,15 +1764,9 @@ pcap_activate_bpf(pcap_t *p) int retv; #endif int fd; -#ifdef LIFNAMSIZ - char *zonesep; +#if defined(LIFNAMSIZ) && defined(ZONENAME_MAX) && defined(lifr_zoneid) struct lifreq ifr; - char *ifrname = ifr.lifr_name; - const size_t ifnamsiz = sizeof(ifr.lifr_name); -#else - struct ifreq ifr; - char *ifrname = ifr.ifr_name; - const size_t ifnamsiz = sizeof(ifr.ifr_name); + char *zonesep; #endif struct bpf_version bv; #ifdef __APPLE__ @@ -1686,7 +1808,7 @@ pcap_activate_bpf(pcap_t *p) } if (bv.bv_major != BPF_MAJOR_VERSION || bv.bv_minor < BPF_MINOR_VERSION) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "kernel bpf filter out of date"); status = PCAP_ERROR; goto bad; @@ -1726,7 +1848,7 @@ pcap_activate_bpf(pcap_t *p) char *lnamep; if (ifr.lifr_zoneid != GLOBAL_ZONEID) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "zonename/linkname only valid in global zone."); status = PCAP_ERROR; goto bad; @@ -1797,24 +1919,19 @@ pcap_activate_bpf(pcap_t *p) */ sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd != -1) { - pcap_strlcpy(ifrname, - p->opt.device, ifnamsiz); - if (ioctl(sockfd, SIOCGIFFLAGS, - (char *)&ifr) < 0) { + status = device_exists(sockfd, + p->opt.device, p->errbuf); + if (status == 0) { /* - * We assume this - * failed because - * the underlying - * device doesn't - * exist. + * The device exists, + * but it's not an + * enN device; that + * means it doesn't + * support monitor + * mode. */ - status = PCAP_ERROR_NO_SUCH_DEVICE; - pcap_fmt_errmsg_for_errno(p->errbuf, - PCAP_ERRBUF_SIZE, - errno, - "SIOCGIFFLAGS failed"); - } else status = PCAP_ERROR_RFMON_NOTSUP; + } close(sockfd); } else { /* @@ -1866,6 +1983,7 @@ pcap_activate_bpf(pcap_t *p) * it when the pcap_t is closed. */ int s; + struct ifreq ifr; /* * Open a socket to use for ioctls to @@ -1889,7 +2007,7 @@ pcap_activate_bpf(pcap_t *p) * "atexit()" failed; don't create the * interface, just give up. */ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "atexit failed"); close(s); status = PCAP_ERROR; @@ -1902,7 +2020,7 @@ pcap_activate_bpf(pcap_t *p) pcap_strlcpy(ifr.ifr_name, p->opt.device, sizeof(ifr.ifr_name)); if (ioctl(s, SIOCIFCREATE2, &ifr) < 0) { if (errno == EINVAL) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Invalid USB bus interface %s", p->opt.device); } else { @@ -1994,11 +2112,19 @@ pcap_activate_bpf(pcap_t *p) status = PCAP_ERROR; goto bad; } - (void)strncpy(ifrname, p->opt.device, ifnamsiz); - if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) < 0) { - pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, - errno, "BIOCSETIF: %s", p->opt.device); - status = PCAP_ERROR; + status = bpf_bind(fd, p->opt.device, ifnamsiz, p->errbuf); + if (status != BPF_BIND_SUCCEEDED) { + if (status == BPF_BIND_BUFFER_TOO_BIG) { + /* + * The requested buffer size + * is too big. Fail. + * + * XXX - should we do the "keep cutting + * the buffer size in half" loop here if + * we're using the default buffer size? + */ + status = PCAP_ERROR; + } goto bad; } v = pb->zbufsize - sizeof(struct bpf_zbuf_header); @@ -2025,14 +2151,23 @@ pcap_activate_bpf(pcap_t *p) /* * Now bind to the device. */ - (void)strncpy(ifrname, p->opt.device, ifnamsiz); -#ifdef BIOCSETLIF - if (ioctl(fd, BIOCSETLIF, (caddr_t)&ifr) < 0) -#else - if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) < 0) -#endif - { - status = check_setif_failure(p, errno); + status = bpf_bind(fd, p->opt.device, p->errbuf); + if (status != BPF_BIND_SUCCEEDED) { + if (status == BPF_BIND_BUFFER_TOO_BIG) { + /* + * The requested buffer size + * is too big. Fail. + */ + status = PCAP_ERROR; + goto bad; + } + + /* + * Special checks on macOS to deal with + * the way monitor mode was done on + * 10.4 Tiger. + */ + status = check_setif_failure(p, status); goto bad; } } else { @@ -2058,22 +2193,30 @@ pcap_activate_bpf(pcap_t *p) */ (void) ioctl(fd, BIOCSBLEN, (caddr_t)&v); - (void)strncpy(ifrname, p->opt.device, ifnamsiz); -#ifdef BIOCSETLIF - if (ioctl(fd, BIOCSETLIF, (caddr_t)&ifr) >= 0) -#else - if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) >= 0) -#endif + status = bpf_bind(fd, p->opt.device, p->errbuf); + if (status == BPF_BIND_SUCCEEDED) break; /* that size worked; we're done */ - if (errno != ENOBUFS) { - status = check_setif_failure(p, errno); + /* + * If the attempt failed because the + * buffer was too big, cut the buffer + * size in half and try again. + * + * Otherwise, fail. + */ + if (status != BPF_BIND_BUFFER_TOO_BIG) { + /* + * Special checks on macOS to deal + * with the way monitor mode was + * done on 10.4 Tiger. + */ + status = check_setif_failure(p, status); goto bad; } } if (v == 0) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCSBLEN: %s: No buffer size worked", p->opt.device); status = PCAP_ERROR; @@ -2117,7 +2260,7 @@ pcap_activate_bpf(pcap_t *p) /* * We don't know what to map this to yet. */ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "unknown interface type %u", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "unknown interface type %u", v); status = PCAP_ERROR; goto bad; @@ -2177,9 +2320,9 @@ pcap_activate_bpf(pcap_t *p) * we try to select DLT_IEEE802_11. */ if (have_osinfo) { - if (isdigit((unsigned)osinfo.release[0]) && + if (PCAP_ISDIGIT((unsigned)osinfo.release[0]) && (osinfo.release[0] == '9' || - isdigit((unsigned)osinfo.release[1]))) { + PCAP_ISDIGIT((unsigned)osinfo.release[1]))) { /* * 10.5 (Darwin 9.x), or later. */ @@ -2429,7 +2572,7 @@ pcap_activate_bpf(pcap_t *p) /* * We don't support immediate mode. Fail. */ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Immediate mode not supported"); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Immediate mode not supported"); status = PCAP_ERROR; goto bad; } @@ -2756,10 +2899,14 @@ get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf) return (-1); } memset(&req, 0, sizeof(req)); - strncpy(req.ifm_name, name, sizeof(req.ifm_name)); + pcap_strlcpy(req.ifm_name, name, sizeof(req.ifm_name)); if (ioctl(sock, SIOCGIFMEDIA, &req) < 0) { if (errno == EOPNOTSUPP || errno == EINVAL || errno == ENOTTY || - errno == ENODEV || errno == EPERM) { + errno == ENODEV || errno == EPERM +#ifdef EPWROFF + || errno == EPWROFF +#endif + ) { /* * Not supported, so we can't provide any * additional information. Assume that @@ -2774,6 +2921,18 @@ get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf) * So, just as we do for some ethtool ioctls * on Linux, which makes the same mistake, we * also treat EPERM as meaning "not supported". + * + * And it appears that Apple's llw0 device, which + * appears to be part of the Skywalk subsystem: + * + * http://newosxbook.com/bonus/vol1ch16.html + * + * can sometimes return EPWROFF ("Device power + * is off") for that ioctl, so we treat *that* + * as another indication that we can't get a + * connection status. (If it *isn't* "powered + * off", it's reported as a wireless device, + * complete with an active/inactive state.) */ *flags |= PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE; close(sock); @@ -2822,7 +2981,7 @@ get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf) } #else static int -get_if_flags(const char *name _U_, bpf_u_int32 *flags _U_, char *errbuf _U_) +get_if_flags(const char *name _U_, bpf_u_int32 *flags, char *errbuf _U_) { /* * Nothing we can do other than mark loopback devices as "the @@ -2883,7 +3042,7 @@ monitor_mode(pcap_t *p, int set) } memset(&req, 0, sizeof req); - strncpy(req.ifm_name, p->opt.device, sizeof req.ifm_name); + pcap_strlcpy(req.ifm_name, p->opt.device, sizeof req.ifm_name); /* * Find out how many media types we have. @@ -2897,7 +3056,11 @@ monitor_mode(pcap_t *p, int set) case ENXIO: /* * There's no such device. + * + * There's nothing more to say, so clear the + * error message. */ + p->errbuf[0] = '\0'; close(sock); return (PCAP_ERROR_NO_SUCH_DEVICE); @@ -2993,7 +3156,7 @@ monitor_mode(pcap_t *p, int set) return (PCAP_ERROR); } memset(&ifr, 0, sizeof(ifr)); - (void)strncpy(ifr.ifr_name, p->opt.device, + (void)pcap_strlcpy(ifr.ifr_name, p->opt.device, sizeof(ifr.ifr_name)); ifr.ifr_media = req.ifm_current | IFM_IEEE80211_MONITOR; if (ioctl(sock, SIOCSIFMEDIA, &ifr) == -1) { @@ -3259,14 +3422,101 @@ static int pcap_setdirection_bpf(pcap_t *p, pcap_direction_t d) { u_int direction; + const char *direction_name; + + /* + * FreeBSD and NetBSD. + */ + switch (d) { + + case PCAP_D_IN: + /* + * Incoming, but not outgoing, so accept only + * incoming packets. + */ + direction = BPF_D_IN; + direction_name = "\"incoming only\""; + break; + + case PCAP_D_OUT: + /* + * Outgoing, but not incoming, so accept only + * outgoing packets. + */ + direction = BPF_D_OUT; + direction_name = "\"outgoing only\""; + break; + + default: + /* + * Incoming and outgoing, so accept both + * incoming and outgoing packets. + * + * It's guaranteed, at this point, that d is a valid + * direction value, so we know that this is PCAP_D_INOUT + * if it's not PCAP_D_IN or PCAP_D_OUT. + */ + direction = BPF_D_INOUT; + direction_name = "\"incoming and outgoing\""; + break; + } - direction = (d == PCAP_D_IN) ? BPF_D_IN : - ((d == PCAP_D_OUT) ? BPF_D_OUT : BPF_D_INOUT); if (ioctl(p->fd, BIOCSDIRECTION, &direction) == -1) { pcap_fmt_errmsg_for_errno(p->errbuf, sizeof(p->errbuf), - errno, "Cannot set direction to %s", - (d == PCAP_D_IN) ? "PCAP_D_IN" : - ((d == PCAP_D_OUT) ? "PCAP_D_OUT" : "PCAP_D_INOUT")); + errno, "Cannot set direction to %s", direction_name); + return (-1); + } + return (0); +} +#elif defined(BIOCSDIRFILT) +static int +pcap_setdirection_bpf(pcap_t *p, pcap_direction_t d) +{ + u_int dirfilt; + const char *direction_name; + + /* + * OpenBSD; same functionality, different names, different + * semantics (the flags mean "*don't* capture packets in + * that direction", not "*capture only* packets in that + * direction"). + */ + switch (d) { + + case PCAP_D_IN: + /* + * Incoming, but not outgoing, so filter out + * outgoing packets. + */ + dirfilt = BPF_DIRECTION_OUT; + direction_name = "\"incoming only\""; + break; + + case PCAP_D_OUT: + /* + * Outgoing, but not incoming, so filter out + * incoming packets. + */ + dirfilt = BPF_DIRECTION_IN; + direction_name = "\"outgoing only\""; + break; + + default: + /* + * Incoming and outgoing, so don't filter out + * any packets based on direction. + * + * It's guaranteed, at this point, that d is a valid + * direction value, so we know that this is PCAP_D_INOUT + * if it's not PCAP_D_IN or PCAP_D_OUT. + */ + dirfilt = 0; + direction_name = "\"incoming and outgoing\""; + break; + } + if (ioctl(p->fd, BIOCSDIRFILT, &dirfilt) == -1) { + pcap_fmt_errmsg_for_errno(p->errbuf, sizeof(p->errbuf), + errno, "Cannot set direction to %s", direction_name); return (-1); } return (0); @@ -3276,21 +3526,47 @@ static int pcap_setdirection_bpf(pcap_t *p, pcap_direction_t d) { u_int seesent; + const char *direction_name; /* - * We don't support PCAP_D_OUT. + * OS with just BIOCSSEESENT. */ - if (d == PCAP_D_OUT) { - pcap_snprintf(p->errbuf, sizeof(p->errbuf), - "Setting direction to PCAP_D_OUT is not supported on BPF"); - return -1; + switch (d) { + + case PCAP_D_IN: + /* + * Incoming, but not outgoing, so we don't want to + * see transmitted packets. + */ + seesent = 0; + direction_name = "\"incoming only\""; + break; + + case PCAP_D_OUT: + /* + * Outgoing, but not incoming; we can't specify that. + */ + snprintf(p->errbuf, sizeof(p->errbuf), + "Setting direction to \"outgoing only\" is not supported on this device"); + return (-1); + + default: + /* + * Incoming and outgoing, so we want to see transmitted + * packets. + * + * It's guaranteed, at this point, that d is a valid + * direction value, so we know that this is PCAP_D_INOUT + * if it's not PCAP_D_IN or PCAP_D_OUT. + */ + seesent = 1; + direction_name = "\"incoming and outgoing\""; + break; } - seesent = (d == PCAP_D_INOUT); if (ioctl(p->fd, BIOCSSEESENT, &seesent) == -1) { pcap_fmt_errmsg_for_errno(p->errbuf, sizeof(p->errbuf), - errno, "Cannot set direction to %s", - (d == PCAP_D_INOUT) ? "PCAP_D_INOUT" : "PCAP_D_IN"); + errno, "Cannot set direction to %s", direction_name); return (-1); } return (0); @@ -3299,8 +3575,8 @@ pcap_setdirection_bpf(pcap_t *p, pcap_direction_t d) static int pcap_setdirection_bpf(pcap_t *p, pcap_direction_t d _U_) { - (void) pcap_snprintf(p->errbuf, sizeof(p->errbuf), - "This system doesn't support BIOCSSEESENT, so the direction can't be set"); + (void) snprintf(p->errbuf, sizeof(p->errbuf), + "Setting direction is not supported on this device"); return (-1); } #endif diff --git a/pcap-bt-linux.c b/pcap-bt-linux.c index 9c8712e9531a..37c805658253 100644 --- a/pcap-bt-linux.c +++ b/pcap-bt-linux.c @@ -58,7 +58,7 @@ /* forward declaration */ static int bt_activate(pcap_t *); static int bt_read_linux(pcap_t *, int , pcap_handler , u_char *); -static int bt_inject_linux(pcap_t *, const void *, size_t); +static int bt_inject_linux(pcap_t *, const void *, int); static int bt_setdirection_linux(pcap_t *, pcap_direction_t); static int bt_stats_linux(pcap_t *, struct pcap_stat *); @@ -92,12 +92,20 @@ bt_findalldevs(pcap_if_list_t *devlistp, char *err_str) dev_list = malloc(HCI_MAX_DEV * sizeof(*dev_req) + sizeof(*dev_list)); if (!dev_list) { - pcap_snprintf(err_str, PCAP_ERRBUF_SIZE, "Can't allocate %zu bytes for Bluetooth device list", + snprintf(err_str, PCAP_ERRBUF_SIZE, "Can't allocate %zu bytes for Bluetooth device list", HCI_MAX_DEV * sizeof(*dev_req) + sizeof(*dev_list)); ret = -1; goto done; } + /* + * Zero the complete header, which is larger than dev_num because of tail + * padding, to silence Valgrind, which overshoots validating that dev_num + * has been set. + * https://github.com/the-tcpdump-group/libpcap/issues/1083 + * https://bugs.kde.org/show_bug.cgi?id=448464 + */ + memset(dev_list, 0, sizeof(*dev_list)); dev_list->dev_num = HCI_MAX_DEV; if (ioctl(sock, HCIGETDEVLIST, (void *) dev_list) < 0) @@ -112,8 +120,8 @@ bt_findalldevs(pcap_if_list_t *devlistp, char *err_str) for (i = 0; i < dev_list->dev_num; i++, dev_req++) { char dev_name[20], dev_descr[40]; - pcap_snprintf(dev_name, sizeof(dev_name), BT_IFACE"%u", dev_req->dev_id); - pcap_snprintf(dev_descr, sizeof(dev_descr), "Bluetooth adapter number %u", i); + snprintf(dev_name, sizeof(dev_name), BT_IFACE"%u", dev_req->dev_id); + snprintf(dev_descr, sizeof(dev_descr), "Bluetooth adapter number %u", i); /* * Bluetooth is a wireless technology. @@ -173,7 +181,7 @@ bt_create(const char *device, char *ebuf, int *is_ours) /* OK, it's probably ours. */ *is_ours = 1; - p = pcap_create_common(ebuf, sizeof (struct pcap_bt)); + p = PCAP_CREATE_COMMON(ebuf, struct pcap_bt); if (p == NULL) return (NULL); @@ -194,7 +202,7 @@ bt_activate(pcap_t* handle) /* get bt interface id */ if (sscanf(handle->opt.device, BT_IFACE"%d", &dev_id) != 1) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't get Bluetooth device index from %s", handle->opt.device); return PCAP_ERROR; @@ -341,12 +349,16 @@ bt_read_linux(pcap_t *handle, int max_packets _U_, pcap_handler callback, u_char } while ((ret == -1) && (errno == EINTR)); if (ret < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + /* Nonblocking mode, no data */ + return 0; + } pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, "Can't receive packet"); return -1; } - pkth.caplen = ret; + pkth.caplen = (bpf_u_int32)ret; /* get direction and timestamp*/ cmsg = CMSG_FIRSTHDR(&msg); @@ -362,15 +374,27 @@ bt_read_linux(pcap_t *handle, int max_packets _U_, pcap_handler callback, u_char } cmsg = CMSG_NXTHDR(&msg, cmsg); } - if ((in && (handle->direction == PCAP_D_OUT)) || - ((!in) && (handle->direction == PCAP_D_IN))) - return 0; + switch (handle->direction) { + + case PCAP_D_IN: + if (!in) + return 0; + break; + + case PCAP_D_OUT: + if (in) + return 0; + break; + + default: + break; + } bthdr->direction = htonl(in != 0); pkth.caplen+=sizeof(pcap_bluetooth_h4_header); pkth.len = pkth.caplen; if (handle->fcode.bf_insns == NULL || - bpf_filter(handle->fcode.bf_insns, pktd, pkth.len, pkth.caplen)) { + pcap_filter(handle->fcode.bf_insns, pktd, pkth.len, pkth.caplen)) { callback(user, &pkth, pktd); return 1; } @@ -378,9 +402,9 @@ bt_read_linux(pcap_t *handle, int max_packets _U_, pcap_handler callback, u_char } static int -bt_inject_linux(pcap_t *handle, const void *buf _U_, size_t size _U_) +bt_inject_linux(pcap_t *handle, const void *buf _U_, int size _U_) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Packet injection is not supported on Bluetooth devices"); return (-1); } @@ -418,6 +442,10 @@ bt_stats_linux(pcap_t *handle, struct pcap_stat *stats) static int bt_setdirection_linux(pcap_t *p, pcap_direction_t d) { + /* + * It's guaranteed, at this point, that d is a valid + * direction value. + */ p->direction = d; return 0; } diff --git a/pcap-bt-monitor-linux.c b/pcap-bt-monitor-linux.c index a693949d4618..206e65b52122 100644 --- a/pcap-bt-monitor-linux.c +++ b/pcap-bt-monitor-linux.c @@ -49,6 +49,14 @@ #define BT_CONTROL_SIZE 32 #define INTERFACE_NAME "bluetooth-monitor" +/* + * Private data. + * Currently contains nothing. + */ +struct pcap_bt_monitor { + int dummy; +}; + /* * Fields and alignment must match the declaration in the Linux kernel 3.4+. * See struct hci_mon_hdr in include/net/bluetooth/hci_mon.h. @@ -119,12 +127,16 @@ bt_monitor_read(pcap_t *handle, int max_packets _U_, pcap_handler callback, u_ch } while ((ret == -1) && (errno == EINTR)); if (ret < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + /* Nonblocking mode, no data */ + return 0; + } pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, "Can't receive packet"); return -1; } - pkth.caplen = ret - sizeof(hdr) + sizeof(pcap_bluetooth_linux_monitor_header); + pkth.caplen = (bpf_u_int32)(ret - sizeof(hdr) + sizeof(pcap_bluetooth_linux_monitor_header)); pkth.len = pkth.caplen; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { @@ -139,7 +151,7 @@ bt_monitor_read(pcap_t *handle, int max_packets _U_, pcap_handler callback, u_ch bthdr->opcode = htons(hdr.opcode); if (handle->fcode.bf_insns == NULL || - bpf_filter(handle->fcode.bf_insns, pktd, pkth.len, pkth.caplen)) { + pcap_filter(handle->fcode.bf_insns, pktd, pkth.len, pkth.caplen)) { callback(user, &pkth, pktd); return 1; } @@ -147,20 +159,13 @@ bt_monitor_read(pcap_t *handle, int max_packets _U_, pcap_handler callback, u_ch } static int -bt_monitor_inject(pcap_t *handle, const void *buf _U_, size_t size _U_) +bt_monitor_inject(pcap_t *handle, const void *buf _U_, int size _U_) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Packet injection is not supported yet on Bluetooth monitor devices"); return -1; } -static int -bt_monitor_setdirection(pcap_t *p, pcap_direction_t d) -{ - p->direction = d; - return 0; -} - static int bt_monitor_stats(pcap_t *handle _U_, struct pcap_stat *stats) { @@ -200,7 +205,7 @@ bt_monitor_activate(pcap_t* handle) handle->read_op = bt_monitor_read; handle->inject_op = bt_monitor_inject; handle->setfilter_op = install_bpf_program; /* no kernel filtering */ - handle->setdirection_op = bt_monitor_setdirection; + handle->setdirection_op = NULL; /* Not implemented */ handle->set_datalink_op = NULL; /* can't change data link type */ handle->getnonblock_op = pcap_getnonblock_fd; handle->setnonblock_op = pcap_setnonblock_fd; @@ -263,7 +268,7 @@ bt_monitor_create(const char *device, char *ebuf, int *is_ours) } *is_ours = 1; - p = pcap_create_common(ebuf, 0); + p = PCAP_CREATE_COMMON(ebuf, struct pcap_bt_monitor); if (p == NULL) return NULL; diff --git a/pcap-common.c b/pcap-common.c index 2a745f0bf969..4f12d8abb18e 100644 --- a/pcap-common.c +++ b/pcap-common.c @@ -28,11 +28,6 @@ #include #include "pcap-int.h" -#include "extract.h" -#include "pcap/sll.h" -#include "pcap/usb.h" -#include "pcap/nflog.h" -#include "pcap/can_socketcan.h" #include "pcap-common.h" @@ -168,11 +163,20 @@ #define LINKTYPE_ENC 109 /* OpenBSD IPSEC enc */ /* - * These three types are reserved for future use. + * These two types are reserved for future use. */ #define LINKTYPE_LANE8023 110 /* ATM LANE + 802.3 */ #define LINKTYPE_HIPPI 111 /* NetBSD HIPPI */ -#define LINKTYPE_HDLC 112 /* NetBSD HDLC framing */ + +/* + * Used for NetBSD DLT_HDLC; from looking at the one driver in NetBSD + * that uses it, it's Cisco HDLC, so it's the same as DLT_C_HDLC/ + * LINKTYPE_C_HDLC, but we define a separate value to avoid some + * compatibility issues with programs on NetBSD. + * + * All code should treat LINKTYPE_NETBSD_HDLC and LINKTYPE_C_HDLC the same. + */ +#define LINKTYPE_NETBSD_HDLC 112 /* NetBSD HDLC framing */ #define LINKTYPE_LINUX_SLL 113 /* Linux cooked socket capture */ #define LINKTYPE_LTALK 114 /* Apple LocalTalk hardware */ @@ -326,7 +330,7 @@ * input packets such as port scans, packets from old lost connections, * etc. to force the connection to stay up). * - * The first byte of the PPP header (0xff03) is modified to accomodate + * The first byte of the PPP header (0xff03) is modified to accommodate * the direction - 0x00 = IN, 0x01 = OUT. */ #define LINKTYPE_PPP_PPPD 166 @@ -361,7 +365,7 @@ /* * Link types requested by Gregor Maier of Endace * Measurement Systems. They add an ERF header (see - * http://www.endace.com/support/EndaceRecordFormat.pdf) in front of + * https://www.endace.com/support/EndaceRecordFormat.pdf) in front of * the link-layer header. */ #define LINKTYPE_ERF_ETH 175 /* Ethernet */ @@ -405,7 +409,7 @@ * DLT_ requested by Gianluca Varenni . * Every frame contains a 32bit A429 label. * More documentation on Arinc 429 can be found at - * http://www.condoreng.com/support/downloads/tutorials/ARINCTutorial.pdf + * https://web.archive.org/web/20040616233302/https://www.condoreng.com/support/downloads/tutorials/ARINCTutorial.pdf */ #define LINKTYPE_A429 184 @@ -495,7 +499,7 @@ /* * Various link-layer types, with a pseudo-header, for SITA - * (http://www.sita.aero/); requested by Fulko Hew (fulko.hew@gmail.com). + * (https://www.sita.aero/); requested by Fulko Hew (fulko.hew@gmail.com). */ #define LINKTYPE_SITA 196 @@ -558,7 +562,6 @@ */ #define LINKTYPE_LAPD 203 - /* * PPP, with a one-byte direction pseudo-header prepended - zero means * "received by this host", non-zero (any non-zero value) means "sent by @@ -608,7 +611,7 @@ /* * Media Oriented Systems Transport (MOST) bus for multimedia - * transport - http://www.mostcooperation.com/ - as requested + * transport - https://www.mostcooperation.com/ - as requested * by Hannes Kaelber . */ #define LINKTYPE_MOST 211 @@ -794,16 +797,16 @@ /* * Raw D-Bus: * - * http://www.freedesktop.org/wiki/Software/dbus + * https://www.freedesktop.org/wiki/Software/dbus * * messages: * - * http://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-messages + * https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-messages * * starting with the endianness flag, followed by the message type, etc., * but without the authentication handshake before the message sequence: * - * http://dbus.freedesktop.org/doc/dbus-specification.html#auth-protocol + * https://dbus.freedesktop.org/doc/dbus-specification.html#auth-protocol * * Requested by Martin Vidner . */ @@ -821,7 +824,7 @@ * DVB-CI (DVB Common Interface for communication between a PC Card * module and a DVB receiver). See * - * http://www.kaiser.cx/pcap-dvbci.html + * https://www.kaiser.cx/pcap-dvbci.html * * for the specification. * @@ -945,7 +948,7 @@ * * Requested by Chris Bontje . */ -#define DLT_RTAC_SERIAL 250 +#define LINKTYPE_RTAC_SERIAL 250 /* * Bluetooth Low Energy air interface link-layer packets. @@ -957,13 +960,15 @@ /* * Link-layer header type for upper-protocol layer PDU saves from wireshark. * - * the actual contents are determined by two TAGs stored with each - * packet: - * EXP_PDU_TAG_LINKTYPE the link type (LINKTYPE_ value) of the - * original packet. + * the actual contents are determined by two TAGs, one or more of + * which is stored with each packet: * - * EXP_PDU_TAG_PROTO_NAME the name of the wireshark dissector - * that can make sense of the data stored. + * EXP_PDU_TAG_DISSECTOR_NAME the name of the Wireshark dissector + * that can make sense of the data stored. + * + * EXP_PDU_TAG_HEUR_DISSECTOR_NAME the name of the Wireshark heuristic + * dissector that can make sense of the + * data stored. */ #define LINKTYPE_WIRESHARK_UPPER_PDU 252 @@ -1079,9 +1084,9 @@ /* * per: Stefanha at gmail.com for - * http://lists.sandelman.ca/pipermail/tcpdump-workers/2017-May/000772.html + * https://lists.sandelman.ca/pipermail/tcpdump-workers/2017-May/000772.html * and: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/vsockmon.h - * for: http://qemu-project.org/Features/VirtioVsock + * for: https://qemu-project.org/Features/VirtioVsock */ #define LINKTYPE_VSOCK 271 @@ -1093,7 +1098,7 @@ /* * Excentis DOCSIS 3.1 RF sniffer (XRA-31) * per: bruno.verstuyft at excentis.com - * http://www.xra31.com/xra-header + * https://www.xra31.com/xra-header */ #define LINKTYPE_DOCSIS31_XRA31 273 @@ -1105,7 +1110,7 @@ /* * DisplayPort AUX channel monitoring data as specified by VESA - * DisplayPort(DP) Standard preceeded by a pseudo-header. + * DisplayPort(DP) Standard preceded by a pseudo-header. * per dirk.eibach at gdsys.cc */ #define LINKTYPE_DISPLAYPORT_AUX 275 @@ -1115,7 +1120,84 @@ */ #define LINKTYPE_LINUX_SLL2 276 -#define LINKTYPE_MATCHING_MAX 276 /* highest value in the "matching" range */ +/* + * Sercos Monitor, per Manuel Jacob + */ +#define LINKTYPE_SERCOS_MONITOR 277 + +/* + * OpenVizsla http://openvizsla.org is open source USB analyzer hardware. + * It consists of FPGA with attached USB phy and FTDI chip for streaming + * the data to the host PC. + * + * Current OpenVizsla data encapsulation format is described here: + * https://github.com/matwey/libopenvizsla/wiki/OpenVizsla-protocol-description + * + */ +#define LINKTYPE_OPENVIZSLA 278 + +/* + * The Elektrobit High Speed Capture and Replay (EBHSCR) protocol is produced + * by a PCIe Card for interfacing high speed automotive interfaces. + * + * The specification for this frame format can be found at: + * https://www.elektrobit.com/ebhscr + * + * for Guenter.Ebermann at elektrobit.com + * + */ +#define LINKTYPE_EBHSCR 279 + +/* + * The https://fd.io vpp graph dispatch tracer produces pcap trace files + * in the format documented here: + * https://fdio-vpp.readthedocs.io/en/latest/gettingstarted/developers/vnet.html#graph-dispatcher-pcap-tracing + */ +#define LINKTYPE_VPP_DISPATCH 280 + +/* + * Broadcom Ethernet switches (ROBO switch) 4 bytes proprietary tagging format. + */ +#define LINKTYPE_DSA_TAG_BRCM 281 +#define LINKTYPE_DSA_TAG_BRCM_PREPEND 282 + +/* + * IEEE 802.15.4 with pseudo-header and optional meta-data TLVs, PHY payload + * exactly as it appears in the spec (no padding, no nothing), and FCS if + * specified by FCS Type TLV; requested by James Ko . + * Specification at https://github.com/jkcko/ieee802.15.4-tap + */ +#define LINKTYPE_IEEE802_15_4_TAP 283 + +/* + * Marvell (Ethertype) Distributed Switch Architecture proprietary tagging format. + */ +#define LINKTYPE_DSA_TAG_DSA 284 +#define LINKTYPE_DSA_TAG_EDSA 285 + +/* + * Payload of lawful intercept packets using the ELEE protocol; + * https://socket.hr/draft-dfranusic-opsawg-elee-00.xml + * https://xml2rfc.tools.ietf.org/cgi-bin/xml2rfc.cgi?url=https://socket.hr/draft-dfranusic-opsawg-elee-00.xml&modeAsFormat=html/ascii + */ +#define LINKTYPE_ELEE 286 + +/* + * Serial frames transmitted between a host and a Z-Wave chip. + */ +#define LINKTYPE_Z_WAVE_SERIAL 287 + +/* + * USB 2.0, 1.1, and 1.0 packets as transmitted over the cable. + */ +#define LINKTYPE_USB_2_0 288 + +/* + * ATSC Link-Layer Protocol (A/330) packets. + */ +#define LINKTYPE_ATSC_ALP 289 + +#define LINKTYPE_MATCHING_MAX 289 /* highest value in the "matching" range */ /* * The DLT_ and LINKTYPE_ values in the "matching" range should be the @@ -1144,7 +1226,7 @@ static struct linktype_map { { DLT_ARCNET, LINKTYPE_ARCNET_BSD }, { DLT_SLIP, LINKTYPE_SLIP }, { DLT_PPP, LINKTYPE_PPP }, - { DLT_FDDI, LINKTYPE_FDDI }, + { DLT_FDDI, LINKTYPE_FDDI }, { DLT_SYMANTEC_FIREWALL, LINKTYPE_SYMANTEC_FIREWALL }, /* @@ -1162,6 +1244,7 @@ static struct linktype_map { { DLT_RAW, LINKTYPE_RAW }, { DLT_SLIP_BSDOS, LINKTYPE_SLIP_BSDOS }, { DLT_PPP_BSDOS, LINKTYPE_PPP_BSDOS }, + { DLT_HDLC, LINKTYPE_NETBSD_HDLC }, /* BSD/OS Cisco HDLC */ { DLT_C_HDLC, LINKTYPE_C_HDLC }, @@ -1248,11 +1331,20 @@ linktype_to_dlt(int linktype) return (DLT_PKTAP); /* - * For all other values in the matching range, the LINKTYPE - * value is the same as the DLT value. + * For all other values in the matching range, except for + * LINKTYPE_ATM_CLIP, the LINKTYPE value is the same as + * the DLT value. + * + * LINKTYPE_ATM_CLIP is a special case. DLT_ATM_CLIP is + * not on all platforms, but, so far, there don't appear + * to be any platforms that define it as anything other + * than 19; we define LINKTYPE_ATM_CLIP as something + * other than 19, just in case. That value is in the + * matching range, so we have to check for it. */ if (linktype >= LINKTYPE_MATCHING_MIN && - linktype <= LINKTYPE_MATCHING_MAX) + linktype <= LINKTYPE_MATCHING_MAX && + linktype != LINKTYPE_ATM_CLIP) return (linktype); /* @@ -1265,7 +1357,7 @@ linktype_to_dlt(int linktype) /* * If we don't have an entry for this LINKTYPE, return - * the link type value; it may be a DLT from an older + * the link type value; it may be a DLT from an newer * version of libpcap. */ return linktype; @@ -1280,6 +1372,10 @@ linktype_to_dlt(int linktype) * * https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-messages * + * For DLT_EBHSCR, the maximum is 8MiB, as per + * + * https://www.elektrobit.com/ebhscr + * * For DLT_USBPCAP, the maximum is 1MiB, as per * * https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=15985 @@ -1292,6 +1388,9 @@ max_snaplen_for_dlt(int dlt) case DLT_DBUS: return 128*1024*1024; + case DLT_EBHSCR: + return 8*1024*1024; + case DLT_USBPCAP: return 1024*1024; @@ -1299,286 +1398,3 @@ max_snaplen_for_dlt(int dlt) return MAXIMUM_SNAPLEN; } } - -/* - * DLT_LINUX_SLL packets with a protocol type of LINUX_SLL_P_CAN or - * LINUX_SLL_P_CANFD have SocketCAN headers in front of the payload, - * with the CAN ID being in host byte order. - * - * When reading a DLT_LINUX_SLL capture file, we need to check for those - * packets and convert the CAN ID from the byte order of the host that - * wrote the file to this host's byte order. - */ -static void -swap_linux_sll_header(const struct pcap_pkthdr *hdr, u_char *buf) -{ - u_int caplen = hdr->caplen; - u_int length = hdr->len; - struct sll_header *shdr = (struct sll_header *)buf; - uint16_t protocol; - pcap_can_socketcan_hdr *chdr; - - if (caplen < (u_int) sizeof(struct sll_header) || - length < (u_int) sizeof(struct sll_header)) { - /* Not enough data to have the protocol field */ - return; - } - - protocol = EXTRACT_16BITS(&shdr->sll_protocol); - if (protocol != LINUX_SLL_P_CAN && protocol != LINUX_SLL_P_CANFD) - return; - - /* - * SocketCAN packet; fix up the packet's header. - */ - chdr = (pcap_can_socketcan_hdr *)(buf + sizeof(struct sll_header)); - if (caplen < (u_int) sizeof(struct sll_header) + sizeof(chdr->can_id) || - length < (u_int) sizeof(struct sll_header) + sizeof(chdr->can_id)) { - /* Not enough data to have the CAN ID */ - return; - } - chdr->can_id = SWAPLONG(chdr->can_id); -} - -/* - * The DLT_USB_LINUX and DLT_USB_LINUX_MMAPPED headers are in host - * byte order when capturing (it's supplied directly from a - * memory-mapped buffer shared by the kernel). - * - * When reading a DLT_USB_LINUX or DLT_USB_LINUX_MMAPPED capture file, - * we need to convert it from the byte order of the host that wrote - * the file to this host's byte order. - */ -static void -swap_linux_usb_header(const struct pcap_pkthdr *hdr, u_char *buf, - int header_len_64_bytes) -{ - pcap_usb_header_mmapped *uhdr = (pcap_usb_header_mmapped *)buf; - bpf_u_int32 offset = 0; - - /* - * "offset" is the offset *past* the field we're swapping; - * we skip the field *before* checking to make sure - * the captured data length includes the entire field. - */ - - /* - * The URB id is a totally opaque value; do we really need to - * convert it to the reading host's byte order??? - */ - offset += 8; /* skip past id */ - if (hdr->caplen < offset) - return; - uhdr->id = SWAPLL(uhdr->id); - - offset += 4; /* skip past various 1-byte fields */ - - offset += 2; /* skip past bus_id */ - if (hdr->caplen < offset) - return; - uhdr->bus_id = SWAPSHORT(uhdr->bus_id); - - offset += 2; /* skip past various 1-byte fields */ - - offset += 8; /* skip past ts_sec */ - if (hdr->caplen < offset) - return; - uhdr->ts_sec = SWAPLL(uhdr->ts_sec); - - offset += 4; /* skip past ts_usec */ - if (hdr->caplen < offset) - return; - uhdr->ts_usec = SWAPLONG(uhdr->ts_usec); - - offset += 4; /* skip past status */ - if (hdr->caplen < offset) - return; - uhdr->status = SWAPLONG(uhdr->status); - - offset += 4; /* skip past urb_len */ - if (hdr->caplen < offset) - return; - uhdr->urb_len = SWAPLONG(uhdr->urb_len); - - offset += 4; /* skip past data_len */ - if (hdr->caplen < offset) - return; - uhdr->data_len = SWAPLONG(uhdr->data_len); - - if (uhdr->transfer_type == URB_ISOCHRONOUS) { - offset += 4; /* skip past s.iso.error_count */ - if (hdr->caplen < offset) - return; - uhdr->s.iso.error_count = SWAPLONG(uhdr->s.iso.error_count); - - offset += 4; /* skip past s.iso.numdesc */ - if (hdr->caplen < offset) - return; - uhdr->s.iso.numdesc = SWAPLONG(uhdr->s.iso.numdesc); - } else - offset += 8; /* skip USB setup header */ - - /* - * With the old header, there are no isochronous descriptors - * after the header. - * - * With the new header, the actual number of descriptors in - * the header is not s.iso.numdesc, it's ndesc - only the - * first N descriptors, for some value of N, are put into - * the header, and ndesc is set to the actual number copied. - * In addition, if s.iso.numdesc is negative, no descriptors - * are captured, and ndesc is set to 0. - */ - if (header_len_64_bytes) { - /* - * This is either the "version 1" header, with - * 16 bytes of additional fields at the end, or - * a "version 0" header from a memory-mapped - * capture, with 16 bytes of zeroed-out padding - * at the end. Byte swap them as if this were - * a "version 1" header. - */ - offset += 4; /* skip past interval */ - if (hdr->caplen < offset) - return; - uhdr->interval = SWAPLONG(uhdr->interval); - - offset += 4; /* skip past start_frame */ - if (hdr->caplen < offset) - return; - uhdr->start_frame = SWAPLONG(uhdr->start_frame); - - offset += 4; /* skip past xfer_flags */ - if (hdr->caplen < offset) - return; - uhdr->xfer_flags = SWAPLONG(uhdr->xfer_flags); - - offset += 4; /* skip past ndesc */ - if (hdr->caplen < offset) - return; - uhdr->ndesc = SWAPLONG(uhdr->ndesc); - - if (uhdr->transfer_type == URB_ISOCHRONOUS) { - /* swap the values in struct linux_usb_isodesc */ - usb_isodesc *pisodesc; - uint32_t i; - - pisodesc = (usb_isodesc *)(void *)(buf+offset); - for (i = 0; i < uhdr->ndesc; i++) { - offset += 4; /* skip past status */ - if (hdr->caplen < offset) - return; - pisodesc->status = SWAPLONG(pisodesc->status); - - offset += 4; /* skip past offset */ - if (hdr->caplen < offset) - return; - pisodesc->offset = SWAPLONG(pisodesc->offset); - - offset += 4; /* skip past len */ - if (hdr->caplen < offset) - return; - pisodesc->len = SWAPLONG(pisodesc->len); - - offset += 4; /* skip past padding */ - - pisodesc++; - } - } - } -} - -/* - * The DLT_NFLOG "packets" have a mixture of big-endian and host-byte-order - * data. They begin with a fixed-length header with big-endian fields, - * followed by a set of TLVs, where the type and length are in host - * byte order but the values are either big-endian or are a raw byte - * sequence that's the same regardless of the host's byte order. - * - * When reading a DLT_NFLOG capture file, we need to convert the type - * and length values from the byte order of the host that wrote the - * file to the byte order of this host. - */ -static void -swap_nflog_header(const struct pcap_pkthdr *hdr, u_char *buf) -{ - u_char *p = buf; - nflog_hdr_t *nfhdr = (nflog_hdr_t *)buf; - nflog_tlv_t *tlv; - u_int caplen = hdr->caplen; - u_int length = hdr->len; - uint16_t size; - - if (caplen < (u_int) sizeof(nflog_hdr_t) || - length < (u_int) sizeof(nflog_hdr_t)) { - /* Not enough data to have any TLVs. */ - return; - } - - if (nfhdr->nflog_version != 0) { - /* Unknown NFLOG version */ - return; - } - - length -= sizeof(nflog_hdr_t); - caplen -= sizeof(nflog_hdr_t); - p += sizeof(nflog_hdr_t); - - while (caplen >= sizeof(nflog_tlv_t)) { - tlv = (nflog_tlv_t *) p; - - /* Swap the type and length. */ - tlv->tlv_type = SWAPSHORT(tlv->tlv_type); - tlv->tlv_length = SWAPSHORT(tlv->tlv_length); - - /* Get the length of the TLV. */ - size = tlv->tlv_length; - if (size % 4 != 0) - size += 4 - size % 4; - - /* Is the TLV's length less than the minimum? */ - if (size < sizeof(nflog_tlv_t)) { - /* Yes. Give up now. */ - return; - } - - /* Do we have enough data for the full TLV? */ - if (caplen < size || length < size) { - /* No. */ - return; - } - - /* Skip over the TLV. */ - length -= size; - caplen -= size; - p += size; - } -} - -void -swap_pseudo_headers(int linktype, struct pcap_pkthdr *hdr, u_char *data) -{ - /* - * Convert pseudo-headers from the byte order of - * the host on which the file was saved to our - * byte order, as necessary. - */ - switch (linktype) { - - case DLT_LINUX_SLL: - swap_linux_sll_header(hdr, data); - break; - - case DLT_USB_LINUX: - swap_linux_usb_header(hdr, data, 0); - break; - - case DLT_USB_LINUX_MMAPPED: - swap_linux_usb_header(hdr, data, 1); - break; - - case DLT_NFLOG: - swap_nflog_header(hdr, data); - break; - } -} diff --git a/pcap-common.h b/pcap-common.h index 8795a8297a64..d765c9476326 100644 --- a/pcap-common.h +++ b/pcap-common.h @@ -21,33 +21,8 @@ * pcap-common.h - common code for pcap and pcapng files */ -/* - * We use the "receiver-makes-right" approach to byte order, - * because time is at a premium when we are writing the file. - * In other words, the pcap_file_header and pcap_pkthdr, - * records are written in host byte order. - * Note that the bytes of packet data are written out in the order in - * which they were received, so multi-byte fields in packets are not - * written in host byte order, they're written in whatever order the - * sending machine put them in. - * - * ntoh[ls] aren't sufficient because we might need to swap on a big-endian - * machine (if the file was written in little-end order). - */ -#define SWAPLONG(y) \ - (((((u_int)(y))&0xff)<<24) | \ - ((((u_int)(y))&0xff00)<<8) | \ - ((((u_int)(y))&0xff0000)>>8) | \ - ((((u_int)(y))>>24)&0xff)) -#define SWAPSHORT(y) \ - ((u_short)(((((u_int)(y))&0xff)<<8) | \ - ((((u_int)(y))&0xff00)>>8))) - extern int dlt_to_linktype(int dlt); extern int linktype_to_dlt(int linktype); -extern void swap_pseudo_headers(int linktype, struct pcap_pkthdr *hdr, - u_char *data); - extern u_int max_snaplen_for_dlt(int dlt); diff --git a/pcap-config.1 b/pcap-config.1 index 0388d0f17dd4..87ab0accb9d9 100644 --- a/pcap-config.1 +++ b/pcap-config.1 @@ -68,5 +68,5 @@ dynamically-linked version of libpcap; the .B \-\-static flag causes it to write flags appropriate for compiling with a statically-linked version of libpcap. -.SH "SEE ALSO" -pcap(3PCAP) +.SH SEE ALSO +.BR pcap (3PCAP) diff --git a/pcap-config.in b/pcap-config.in index 54ca42f07bc5..6039ef33b324 100644 --- a/pcap-config.in +++ b/pcap-config.in @@ -12,13 +12,15 @@ prefix="@prefix@" exec_prefix="@exec_prefix@" includedir="@includedir@" libdir="@libdir@" -V_RPATH_OPT="@V_RPATH_OPT@" LIBS="@LIBS@" -PACKAGE_NAME="@PACKAGE_NAME@" +LIBS_STATIC="@LIBS_STATIC@" +VERSION="@PACKAGE_VERSION@" static=0 +static_pcap_only=0 show_cflags=0 show_libs=0 +show_additional_libs=0 while [ "$#" != 0 ] do case "$1" in @@ -27,6 +29,10 @@ do static=1 ;; + --static-pcap-only) + static_pcap_only=1 + ;; + --cflags) show_cflags=1 ;; @@ -38,49 +44,107 @@ do --additional-libs) show_additional_libs=1 ;; + + -h|--help) + echo "Usage: pcap-config [ --help ] [--version] [ --static | --static-pcap-only ] [ --libs | --additional-libs ]" + exit 0 + ;; + + --version) + echo "$VERSION" + exit 0 + ;; + + *) + echo "pcap-config: Invalid command-line option $1 specified" 1>&2 + echo "Usage: pcap-config [ --help ] [ --static | --static-pcap-only ] [ --libs | --additional-libs ]" 1>&2 + exit 1 + ;; esac shift done -if [ "$V_RPATH_OPT" != "" ] + +# +# If we aren't installing in /usr, then provide a -L flag to let build +# processes find our library. +# +# (We must check $prefix, as $libdir isn't necessarily /usr/lib in this +# case - for example, Linux distributions for 64-bit platforms that +# also provide support for binaries for a 32-bit version of the +# platform may put the 64-bit libraries, the 32-bit libraries, or both +# in directories other than /usr/lib.) +# +if [ "$prefix" != "/usr" ] then - # - # If libdir isn't /usr/lib, add it to the run-time linker path. - # - if [ "$libdir" != "/usr/lib" ] - then - RPATH=$V_RPATH_OPT$libdir - fi + LPATH=-L$libdir fi if [ "$static" = 1 ] then # - # Include LIBS so that the flags include libraries containing - # routines that libpcap uses. + # Include LIBS_STATIC so that the flags include libraries + # containing routines that libpcap uses, and libraries + # containing routines those libraries use, etc., so that a + # completely statically linked program - i.e., linked only with + # static libraries - will be linked with all necessary + # libraries. # if [ "$show_cflags" = 1 -a "$show_libs" = 1 ] then - echo "-I$includedir -L$libdir -lpcap $LIBS" + echo "-I$includedir $LPATH -l@PACKAGE_NAME@ $LIBS_STATIC" elif [ "$show_cflags" = 1 -a "$show_additional_libs" = 1 ] then - echo "-I$includedir -L$libdir $LIBS" + echo "-I$includedir $LPATH $LIBS_STATIC" elif [ "$show_cflags" = 1 ] then echo "-I$includedir" elif [ "$show_libs" = 1 ] then - echo "-L$libdir -lpcap $LIBS" + echo "$LPATH -l@PACKAGE_NAME@ $LIBS_STATIC" + elif [ "$show_additional_libs" = 1 ] + then + echo "$LIBS_STATIC" + fi +elif [ "$static_pcap_only" = 1 ] +then + # + # Include LIBS so that the flags include libraries + # containing routines that libpcap uses, but not the libraries + # on which libpcap depends, so that an otherwise + # dynamically-linked program, linked statically only with + # libpcap - i.e., linked with a static libpcap and dynamic + # versions of other libraries - will be linked with all + # necessary libraries. + # + if [ "$show_cflags" = 1 -a "$show_libs" = 1 ] + then + echo "-I$includedir $LPATH -l@PACKAGE_NAME@ $LIBS" + elif [ "$show_cflags" = 1 -a "$show_additional_libs" = 1 ] + then + echo "-I$includedir $LPATH $LIBS" + elif [ "$show_cflags" = 1 ] + then + echo "-I$includedir" + elif [ "$show_libs" = 1 ] + then + echo "$LPATH -l@PACKAGE_NAME@ $LIBS" elif [ "$show_additional_libs" = 1 ] then echo "$LIBS" fi else # - # Omit LIBS - libpcap is assumed to be linked with those - # libraries, so there's no need to do so explicitly. + # Don't included LIBS or LIBS_STATIC, for building a program + # with a dynamic libpcap; libpcap, being a dynamic library, will + # cause all of its dynamic-library dependencies to be pulled in + # at run time. + # + # Do, however, include RPATH, to make sure that, on platforms + # that require this, programs built with this version of + # libpcap can find it at run time. # if [ "$show_cflags" = 1 -a "$show_libs" = 1 ] then - echo "-I$includedir -L$libdir $RPATH -l$PACKAGE_NAME" + echo "-I$includedir $LPATH @RPATH@ -l@PACKAGE_NAME@" elif [ "$show_cflags" = 1 -a "$show_additional_libs" = 1 ] then echo "-I$includedir" @@ -89,6 +153,6 @@ else echo "-I$includedir" elif [ "$show_libs" = 1 ] then - echo "-L$libdir $RPATH -l$PACKAGE_NAME" + echo "$LPATH @RPATH@ -l@PACKAGE_NAME@" fi fi diff --git a/pcap-dag.c b/pcap-dag.c index e076676c45bc..f261ead00df8 100644 --- a/pcap-dag.c +++ b/pcap-dag.c @@ -19,7 +19,6 @@ #include "pcap-int.h" -#include #include #include #include @@ -208,14 +207,13 @@ static unsigned char TempPkt[MAX_DAG_PACKET]; #define dag_size_t uint32_t #endif -static int dag_setfilter(pcap_t *p, struct bpf_program *fp); static int dag_stats(pcap_t *p, struct pcap_stat *ps); static int dag_set_datalink(pcap_t *p, int dlt); static int dag_get_datalink(pcap_t *p); static int dag_setnonblock(pcap_t *p, int nonblock); static void -delete_pcap_dag(pcap_t *p) +delete_pcap_dag(const pcap_t *p) { pcap_dag_node_t *curr = NULL, *prev = NULL; @@ -301,7 +299,7 @@ new_pcap_dag(pcap_t *p) } static unsigned int -dag_erf_ext_header_count(uint8_t * erf, size_t len) +dag_erf_ext_header_count(const uint8_t *erf, size_t len) { uint32_t hdr_num = 0; uint8_t hdr_type; @@ -335,7 +333,7 @@ dag_erf_ext_header_count(uint8_t * erf, size_t len) /* * Read at most max_packets from the capture stream and call the callback * for each of them. Returns the number of packets handled, -1 if an - * error occured, or -2 if we were told to break out of the loop. + * error occurred, or -2 if we were told to break out of the loop. */ static int dag_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) @@ -393,7 +391,12 @@ dag_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) } - /* Process the packets. */ + /* + * Process the packets. + * + * This assumes that a single buffer of packets will have + * <= INT_MAX packets, so the packet count doesn't overflow. + */ while (pd->dag_mem_top - pd->dag_mem_bottom >= dag_record_size) { unsigned short packet_len = 0; @@ -421,7 +424,8 @@ dag_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) rlen = ntohs(header->rlen); if (rlen < dag_record_size) { - strncpy(p->errbuf, "dag_read: record too small", PCAP_ERRBUF_SIZE); + pcap_strlcpy(p->errbuf, "dag_read: record too small", + PCAP_ERRBUF_SIZE); return -1; } pd->dag_mem_bottom += rlen; @@ -664,7 +668,7 @@ dag_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) caplen = p->snapshot; /* Run the packet filter if there is one. */ - if ((p->fcode.bf_insns == NULL) || bpf_filter(p->fcode.bf_insns, dp, packet_len, caplen)) { + if ((p->fcode.bf_insns == NULL) || pcap_filter(p->fcode.bf_insns, dp, packet_len, caplen)) { /* convert between timestamp formats */ register unsigned long long ts; @@ -718,7 +722,7 @@ dag_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) } static int -dag_inject(pcap_t *p, const void *buf _U_, size_t size _U_) +dag_inject(pcap_t *p, const void *buf _U_, int size _U_) { pcap_strlcpy(p->errbuf, "Sending packets isn't supported on DAG cards", PCAP_ERRBUF_SIZE); @@ -732,7 +736,7 @@ dag_inject(pcap_t *p, const void *buf _U_, size_t size _U_) * API polling parameters. * * snaplen is now also ignored, until we get per-stream slen support. Set - * slen with approprite DAG tool BEFORE pcap_activate(). + * slen with appropriate DAG tool BEFORE pcap_activate(). * * See also pcap(3). */ @@ -750,7 +754,7 @@ static int dag_activate(pcap_t* p) struct timeval poll; if (device == NULL) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "device is NULL"); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "device is NULL"); return PCAP_ERROR; } @@ -780,7 +784,7 @@ static int dag_activate(pcap_t* p) if (pd->dag_stream%2) { ret = PCAP_ERROR; - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "dag_parse_name: tx (even numbered) streams not supported for capture"); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "dag_parse_name: tx (even numbered) streams not supported for capture"); goto fail; } @@ -789,14 +793,23 @@ static int dag_activate(pcap_t* p) /* * XXX - does this reliably set errno? */ - if (errno == ENOENT) + if (errno == ENOENT) { + /* + * There's nothing more to say, so clear + * the error message. + */ ret = PCAP_ERROR_NO_SUCH_DEVICE; - else if (errno == EPERM || errno == EACCES) + p->errbuf[0] = '\0'; + } else if (errno == EPERM || errno == EACCES) { ret = PCAP_ERROR_PERM_DENIED; - else + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Attempt to open %s failed with %s - additional privileges may be required", + device, (errno == EPERM) ? "EPERM" : "EACCES"); + } else { ret = PCAP_ERROR; - pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, - errno, "dag_config_init %s", device); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "dag_config_init %s", device); + } goto fail; } @@ -936,7 +949,7 @@ static int dag_activate(pcap_t* p) pd->dag_fcs_bits = n; } else { ret = PCAP_ERROR; - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "pcap_activate %s: bad ERF_FCS_BITS value (%d) in environment", device, n); goto failstop; } @@ -946,7 +959,7 @@ static int dag_activate(pcap_t* p) * Did the user request that they not be stripped? */ if ((s = getenv("ERF_DONT_STRIP_FCS")) != NULL) { - /* Yes. Note the number of bytes that will be + /* Yes. Note the number of 16-bit words that will be supplied. */ p->linktype_ext = LT_FCS_DATALINK_EXT(pd->dag_fcs_bits/16); @@ -983,7 +996,7 @@ static int dag_activate(pcap_t* p) p->read_op = dag_read; p->inject_op = dag_inject; - p->setfilter_op = dag_setfilter; + p->setfilter_op = install_bpf_program; p->setdirection_op = NULL; /* Not implemented.*/ p->set_datalink_op = dag_set_datalink; p->getnonblock_op = pcap_getnonblock_fd; @@ -1072,7 +1085,7 @@ pcap_t *dag_create(const char *device, char *ebuf, int *is_ours) /* OK, it's probably ours. */ *is_ours = 1; - p = pcap_create_common(ebuf, sizeof (struct pcap_dag)); + p = PCAP_CREATE_COMMON(ebuf, struct pcap_dag); if (p == NULL) return NULL; @@ -1085,7 +1098,6 @@ pcap_t *dag_create(const char *device, char *ebuf, int *is_ours) * XXX Our native precision is 2^-32s, but libpcap doesn't support * power of two precisions yet. We can convert to either MICRO or NANO. */ - p->tstamp_precision_count = 2; p->tstamp_precision_list = malloc(2 * sizeof(u_int)); if (p->tstamp_precision_list == NULL) { pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, @@ -1095,6 +1107,7 @@ pcap_t *dag_create(const char *device, char *ebuf, int *is_ours) } p->tstamp_precision_list[0] = PCAP_TSTAMP_PRECISION_MICRO; p->tstamp_precision_list[1] = PCAP_TSTAMP_PRECISION_NANO; + p->tstamp_precision_count = 2; return p; } @@ -1115,10 +1128,10 @@ dag_stats(pcap_t *p, struct pcap_stat *ps) { /* Note this counter is cleared at start of capture and will wrap at UINT_MAX. * The application is responsible for polling ps_drop frequently enough * to detect each wrap and integrate total drop with a wider counter */ - if ((dag_error = dag_config_get_uint32_attribute_ex(pd->dag_ref, pd->drop_attr, &stream_drop) == kDagErrNone)) { + if ((dag_error = dag_config_get_uint32_attribute_ex(pd->dag_ref, pd->drop_attr, &stream_drop)) == kDagErrNone) { pd->stat.ps_drop = stream_drop; } else { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "reading stream drop attribute: %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "reading stream drop attribute: %s", dag_config_strerror(dag_error)); return -1; } @@ -1146,10 +1159,10 @@ dag_findalldevs(pcap_if_list_t *devlistp, char *errbuf) /* Try all the DAGs 0-DAG_MAX_BOARDS */ for (c = 0; c < DAG_MAX_BOARDS; c++) { - pcap_snprintf(name, 12, "dag%d", c); + snprintf(name, 12, "dag%d", c); if (-1 == dag_parse_name(name, dagname, DAGNAME_BUFSIZE, &dagstream)) { - (void) pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + (void) snprintf(errbuf, PCAP_ERRBUF_SIZE, "dag: device name %s can't be parsed", name); return (-1); } @@ -1177,7 +1190,7 @@ dag_findalldevs(pcap_if_list_t *devlistp, char *errbuf) if (0 == dag_attach_stream64(dagfd, stream, 0, 0)) { dag_detach_stream(dagfd, stream); - pcap_snprintf(name, 10, "dag%d:%d", c, stream); + snprintf(name, 10, "dag%d:%d", c, stream); if (add_dev(devlistp, name, 0, description, errbuf) == NULL) { /* * Failure. @@ -1198,30 +1211,6 @@ dag_findalldevs(pcap_if_list_t *devlistp, char *errbuf) return (0); } -/* - * Installs the given bpf filter program in the given pcap structure. There is - * no attempt to store the filter in kernel memory as that is not supported - * with DAG cards. - */ -static int -dag_setfilter(pcap_t *p, struct bpf_program *fp) -{ - if (!p) - return -1; - if (!fp) { - strncpy(p->errbuf, "setfilter: No filter specified", - sizeof(p->errbuf)); - return -1; - } - - /* Make our private copy of the filter */ - - if (install_bpf_program(p, fp) < 0) - return -1; - - return (0); -} - static int dag_set_datalink(pcap_t *p, int dlt) { @@ -1451,7 +1440,7 @@ pcap_platform_finddevs(pcap_if_list_t *devlistp _U_, char *errbuf) pcap_t * pcap_create_interface(const char *device, char *errbuf) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "This version of libpcap only supports DAG cards"); return NULL; } diff --git a/pcap-dbus.c b/pcap-dbus.c index 1252975e9ac7..506f150fa5c5 100644 --- a/pcap-dbus.c +++ b/pcap-dbus.c @@ -68,7 +68,7 @@ dbus_read(pcap_t *handle, int max_packets _U_, pcap_handler callback, u_char *us while (!message) { /* XXX handle->opt.timeout = timeout_ms; */ if (!dbus_connection_read_write(handlep->conn, 100)) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Connection closed"); + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Connection closed"); return -1; } @@ -81,7 +81,7 @@ dbus_read(pcap_t *handle, int max_packets _U_, pcap_handler callback, u_char *us } if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Disconnected"); + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Disconnected"); return -1; } @@ -91,7 +91,7 @@ dbus_read(pcap_t *handle, int max_packets _U_, pcap_handler callback, u_char *us gettimeofday(&pkth.ts, NULL); if (handle->fcode.bf_insns == NULL || - bpf_filter(handle->fcode.bf_insns, (u_char *)raw_msg, pkth.len, pkth.caplen)) { + pcap_filter(handle->fcode.bf_insns, (u_char *)raw_msg, pkth.len, pkth.caplen)) { handlep->packets_read++; callback(user, &pkth, (u_char *)raw_msg); count++; @@ -103,7 +103,7 @@ dbus_read(pcap_t *handle, int max_packets _U_, pcap_handler callback, u_char *us } static int -dbus_write(pcap_t *handle, const void *buf, size_t size) +dbus_write(pcap_t *handle, const void *buf, int size) { /* XXX, not tested */ struct pcap_dbus *handlep = handle->priv; @@ -112,7 +112,7 @@ dbus_write(pcap_t *handle, const void *buf, size_t size) DBusMessage *msg; if (!(msg = dbus_message_demarshal(buf, size, &error))) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "dbus_message_demarshal() failed: %s", error.message); + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "dbus_message_demarshal() failed: %s", error.message); dbus_error_free(&error); return -1; } @@ -154,7 +154,7 @@ dbus_cleanup(pcap_t *handle) static int dbus_getnonblock(pcap_t *p) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Non-blocking mode isn't supported for capturing on D-Bus"); return (-1); } @@ -162,7 +162,7 @@ dbus_getnonblock(pcap_t *p) static int dbus_setnonblock(pcap_t *p, int nonblock _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Non-blocking mode isn't supported for capturing on D-Bus"); return (-1); } @@ -189,14 +189,14 @@ dbus_activate(pcap_t *handle) if (strcmp(dev, "dbus-system") == 0) { if (!(handlep->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error))) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Failed to get system bus: %s", error.message); + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Failed to get system bus: %s", error.message); dbus_error_free(&error); return PCAP_ERROR; } } else if (strcmp(dev, "dbus-session") == 0) { if (!(handlep->conn = dbus_bus_get(DBUS_BUS_SESSION, &error))) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Failed to get session bus: %s", error.message); + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Failed to get session bus: %s", error.message); dbus_error_free(&error); return PCAP_ERROR; } @@ -205,19 +205,19 @@ dbus_activate(pcap_t *handle) const char *addr = dev + 7; if (!(handlep->conn = dbus_connection_open(addr, &error))) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Failed to open connection to: %s: %s", addr, error.message); + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Failed to open connection to: %s: %s", addr, error.message); dbus_error_free(&error); return PCAP_ERROR; } if (!dbus_bus_register(handlep->conn, &error)) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Failed to register bus %s: %s\n", addr, error.message); + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Failed to register bus %s: %s\n", addr, error.message); dbus_error_free(&error); return PCAP_ERROR; } } else { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't get bus address from %s", handle->opt.device); + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't get bus address from %s", handle->opt.device); return PCAP_ERROR; } @@ -289,7 +289,7 @@ dbus_activate(pcap_t *handle) /* try without eavesdrop */ dbus_bus_add_match(handlep->conn, rules[i] + strlen(EAVESDROPPING_RULE), &error); if (dbus_error_is_set(&error)) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Failed to add bus match: %s\n", error.message); + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Failed to add bus match: %s\n", error.message); dbus_error_free(&error); dbus_cleanup(handle); return PCAP_ERROR; @@ -314,7 +314,7 @@ dbus_create(const char *device, char *ebuf, int *is_ours) } *is_ours = 1; - p = pcap_create_common(ebuf, sizeof (struct pcap_dbus)); + p = PCAP_CREATE_COMMON(ebuf, struct pcap_dbus); if (p == NULL) return (NULL); diff --git a/pcap-dll.rc b/pcap-dll.rc index fc4f42b26ef0..85de542f085f 100644 --- a/pcap-dll.rc +++ b/pcap-dll.rc @@ -20,12 +20,12 @@ VALUE "Comments", "https://github.com/the-tcpdump-group/libpcap/" VALUE "CompanyName", "The TCPdump Group" VALUE "FileDescription", "System-Independent Interface for User-Level Packet Capture" - VALUE "FileVersion", "PACKAGE_VERSION_DLL" + VALUE "FileVersion", PACKAGE_VERSION VALUE "InternalName", PACKAGE_NAME VALUE "LegalCopyright", "Copyright (c) The TCPdump Group" VALUE "LegalTrademarks", "" - VALUE "OriginalFilename", "wpcap.dll" - VALUE "ProductName", PACKAGE_NAME + VALUE "OriginalFilename", PACKAGE_NAME ".dll" + VALUE "ProductName", "libpcap" VALUE "ProductVersion", PACKAGE_VERSION END END diff --git a/pcap-dlpi.c b/pcap-dlpi.c index 3ed8fa7a2763..16ed52da51be 100644 --- a/pcap-dlpi.c +++ b/pcap-dlpi.c @@ -96,7 +96,6 @@ #include #endif -#include #ifdef HAVE_HPUX9 #include #endif @@ -108,12 +107,7 @@ #include #include #include - -#ifdef HAVE_LIMITS_H #include -#else -#define INT_MAX 2147483647 -#endif #include "pcap-int.h" #include "dlpisubs.h" @@ -152,7 +146,7 @@ static int dl_dohpuxbind(int, char *); static int dlpromiscon(pcap_t *, bpf_u_int32); static int dlbindreq(int, bpf_u_int32, char *); static int dlbindack(int, char *, char *, int *); -static int dlokack(int, const char *, char *, char *); +static int dlokack(int, const char *, char *, char *, int *); static int dlinforeq(int, char *); static int dlinfoack(int, char *, char *); @@ -252,7 +246,7 @@ pcap_read_dlpi(pcap_t *p, int cnt, pcap_handler callback, u_char *user) } static int -pcap_inject_dlpi(pcap_t *p, const void *buf, size_t size) +pcap_inject_dlpi(pcap_t *p, const void *buf, int size) { #ifdef DL_HP_RAWDLS struct pcap_dlpi *pd = p->priv; @@ -268,7 +262,7 @@ pcap_inject_dlpi(pcap_t *p, const void *buf, size_t size) } #elif defined(DL_HP_RAWDLS) if (pd->send_fd < 0) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send: Output FD couldn't be opened"); return (-1); } @@ -372,8 +366,12 @@ open_dlpi_device(const char *name, u_int *ppa, char *errbuf) * chop off the unit number, so "dname" is just a device type name. */ cp = split_dname(dname, &unit, errbuf); - if (cp == NULL) + if (cp == NULL) { + /* + * split_dname() has filled in the error message. + */ return (PCAP_ERROR_NO_SUCH_DEVICE); + } *cp = '\0'; /* @@ -389,12 +387,16 @@ open_dlpi_device(const char *name, u_int *ppa, char *errbuf) */ cp = "/dev/dlpi"; if ((fd = open(cp, O_RDWR)) < 0) { - if (errno == EPERM || errno == EACCES) + if (errno == EPERM || errno == EACCES) { status = PCAP_ERROR_PERM_DENIED; - else + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "Attempt to open %s failed with %s - root privilege may be required", + cp, (errno == EPERM) ? "EPERM" : "EACCES"); + } else { status = PCAP_ERROR; - pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, - errno, "%s", cp); + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "Attempt to open %s failed", cp); + } return (status); } @@ -417,7 +419,7 @@ open_dlpi_device(const char *name, u_int *ppa, char *errbuf) if (*name == '/') pcap_strlcpy(dname, name, sizeof(dname)); else - pcap_snprintf(dname, sizeof(dname), "%s/%s", PCAP_DEV_PREFIX, + snprintf(dname, sizeof(dname), "%s/%s", PCAP_DEV_PREFIX, name); /* @@ -425,8 +427,12 @@ open_dlpi_device(const char *name, u_int *ppa, char *errbuf) * type name. */ cp = split_dname(dname, ppa, errbuf); - if (cp == NULL) + if (cp == NULL) { + /* + * split_dname() has filled in the error message. + */ return (PCAP_ERROR_NO_SUCH_DEVICE); + } /* * Make a copy of the device pathname, and then remove the unit @@ -438,12 +444,18 @@ open_dlpi_device(const char *name, u_int *ppa, char *errbuf) /* Try device without unit number */ if ((fd = open(dname, O_RDWR)) < 0) { if (errno != ENOENT) { - if (errno == EPERM || errno == EACCES) + if (errno == EPERM || errno == EACCES) { status = PCAP_ERROR_PERM_DENIED; - else + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "Attempt to open %s failed with %s - root privilege may be required", + dname, + (errno == EPERM) ? "EPERM" : "EACCES"); + } else { status = PCAP_ERROR; - pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, - errno, "%s", dname); + pcap_fmt_errmsg_for_errno(errbuf, + PCAP_ERRBUF_SIZE, errno, + "Attempt to open %s failed", dname); + } return (status); } @@ -475,15 +487,22 @@ open_dlpi_device(const char *name, u_int *ppa, char *errbuf) * interface is just a symptom of that * inability. */ - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: No DLPI device found", name); } else { - if (errno == EPERM || errno == EACCES) + if (errno == EPERM || errno == EACCES) { status = PCAP_ERROR_PERM_DENIED; - else + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "Attempt to open %s failed with %s - root privilege may be required", + dname2, + (errno == EPERM) ? "EPERM" : "EACCES"); + } else { status = PCAP_ERROR; - pcap_fmt_errmsg_for_errno(errbuf, - PCAP_ERRBUF_SIZE, errno, "%s", dname2); + pcap_fmt_errmsg_for_errno(errbuf, + PCAP_ERRBUF_SIZE, errno, + "Attempt to open %s failed", + dname2); + } } return (status); } @@ -639,7 +658,7 @@ pcap_activate_dlpi(pcap_t *p) **/ if (dlbindreq(p->fd, 0, p->errbuf) < 0 || dlbindack(p->fd, (char *)buf, p->errbuf, NULL) < 0) { - status = PCAP_ERROR; + status = PCAP_ERROR; goto bad; } #endif /* AIX vs. HP-UX vs. other */ @@ -767,7 +786,7 @@ pcap_activate_dlpi(pcap_t *p) */ if (dlinforeq(p->fd, p->errbuf) < 0 || dlinfoack(p->fd, (char *)buf, p->errbuf) < 0) { - status = PCAP_ERROR; + status = PCAP_ERROR; goto bad; } @@ -805,7 +824,7 @@ pcap_activate_dlpi(pcap_t *p) get_release(release, sizeof (release), &osmajor, &osminor, &osmicro); if (osmajor == 5 && (osminor <= 2 || (osminor == 3 && osmicro < 2)) && getenv("BUFMOD_FIXED") == NULL) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "WARNING: bufmod is broken in SunOS %s; ignoring snaplen.", release); ss = 0; @@ -880,7 +899,7 @@ split_dname(char *device, u_int *unitp, char *ebuf) */ cp = device + strlen(device) - 1; if (*cp < '0' || *cp > '9') { - pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, "%s missing unit number", + snprintf(ebuf, PCAP_ERRBUF_SIZE, "%s missing unit number", device); return (NULL); } @@ -892,16 +911,16 @@ split_dname(char *device, u_int *unitp, char *ebuf) errno = 0; unit = strtol(cp, &eos, 10); if (*eos != '\0') { - pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, "%s bad unit number", device); + snprintf(ebuf, PCAP_ERRBUF_SIZE, "%s bad unit number", device); return (NULL); } if (errno == ERANGE || unit > INT_MAX) { - pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, "%s unit number too large", + snprintf(ebuf, PCAP_ERRBUF_SIZE, "%s unit number too large", device); return (NULL); } if (unit < 0) { - pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, "%s unit number is negative", + snprintf(ebuf, PCAP_ERRBUF_SIZE, "%s unit number is negative", device); return (NULL); } @@ -921,7 +940,7 @@ dl_doattach(int fd, int ppa, char *ebuf) if (send_request(fd, (char *)&req, sizeof(req), "attach", ebuf) < 0) return (PCAP_ERROR); - err = dlokack(fd, "attach", (char *)buf, ebuf); + err = dlokack(fd, "attach", (char *)buf, ebuf, NULL); if (err < 0) return (err); return (0); @@ -986,6 +1005,7 @@ dlpromiscon(pcap_t *p, bpf_u_int32 level) dl_promiscon_req_t req; bpf_u_int32 buf[MAXDLBUF]; int err; + int uerror; req.dl_primitive = DL_PROMISCON_REQ; req.dl_level = level; @@ -993,9 +1013,16 @@ dlpromiscon(pcap_t *p, bpf_u_int32 level) p->errbuf) < 0) return (PCAP_ERROR); err = dlokack(p->fd, "promiscon" STRINGIFY(level), (char *)buf, - p->errbuf); - if (err < 0) + p->errbuf, &uerror); + if (err < 0) { + if (err == PCAP_ERROR_PERM_DENIED) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Attempt to set promiscuous mode failed with %s - root privilege may be required", + (uerror == EPERM) ? "EPERM" : "EACCES"); + err = PCAP_ERROR_PROMISC_PERM_DENIED; + } return (err); + } return (0); } @@ -1115,7 +1142,7 @@ pcap_platform_finddevs(pcap_if_list_t *devlistp, char *errbuf) return (-1); } for (i = 0; i < buf.nunits; i++) { - pcap_snprintf(baname, sizeof baname, "ba%u", i); + snprintf(baname, sizeof baname, "ba%u", i); /* * XXX - is there a notion of "up" and "running"? * And is there a way to determine whether the @@ -1202,7 +1229,10 @@ recv_ack(int fd, int size, const char *what, char *bufp, char *ebuf, int *uerror break; default: - pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, + /* + * Neither EPERM nor EACCES. + */ + snprintf(ebuf, PCAP_ERRBUF_SIZE, "recv_ack: %s: %s", what, dlstrerror(errmsgbuf, sizeof (errmsgbuf), dlp->error_ack.dl_errno)); if (dlp->error_ack.dl_errno == DL_BADPPA) @@ -1214,14 +1244,14 @@ recv_ack(int fd, int size, const char *what, char *bufp, char *ebuf, int *uerror return (PCAP_ERROR); default: - pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, + snprintf(ebuf, PCAP_ERRBUF_SIZE, "recv_ack: %s: Unexpected primitive ack %s", what, dlprim(dlprimbuf, sizeof (dlprimbuf), dlp->dl_primitive)); return (PCAP_ERROR); } if (ctl.len < size) { - pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, + snprintf(ebuf, PCAP_ERRBUF_SIZE, "recv_ack: %s: Ack too small (%d < %d)", what, ctl.len, size); return (PCAP_ERROR); @@ -1332,7 +1362,7 @@ dlstrerror(char *errbuf, size_t errbufsize, bpf_u_int32 dl_errno) return ("Pending outstanding connect indications"); default: - pcap_snprintf(errbuf, errbufsize, "Error %02x", dl_errno); + snprintf(errbuf, errbufsize, "Error %02x", dl_errno); return (errbuf); } } @@ -1424,7 +1454,7 @@ dlprim(char *primbuf, size_t primbufsize, bpf_u_int32 prim) return ("DL_RESET_CON"); default: - pcap_snprintf(primbuf, primbufsize, "unknown primitive 0x%x", + snprintf(primbuf, primbufsize, "unknown primitive 0x%x", prim); return (primbuf); } @@ -1458,10 +1488,10 @@ dlbindack(int fd, char *bufp, char *ebuf, int *uerror) } static int -dlokack(int fd, const char *what, char *bufp, char *ebuf) +dlokack(int fd, const char *what, char *bufp, char *ebuf, int *uerror) { - return (recv_ack(fd, DL_OK_ACK_SIZE, what, bufp, ebuf, NULL)); + return (recv_ack(fd, DL_OK_ACK_SIZE, what, bufp, ebuf, uerror)); } @@ -1496,7 +1526,7 @@ dlpassive(int fd, char *ebuf) req.dl_primitive = DL_PASSIVE_REQ; if (send_request(fd, (char *)&req, sizeof(req), "dlpassive", ebuf) == 0) - (void) dlokack(fd, "dlpassive", (char *)buf, ebuf); + (void) dlokack(fd, "dlpassive", (char *)buf, ebuf, NULL); } #endif @@ -1551,7 +1581,7 @@ get_release(char *buf, size_t bufsize, bpf_u_int32 *majorp, return; } cp = buf; - if (!isdigit((unsigned char)*cp)) + if (!PCAP_ISDIGIT((unsigned char)*cp)) return; *majorp = strtol(cp, &cp, 10); if (*cp++ != '.') @@ -1651,21 +1681,21 @@ get_dlpi_ppa(register int fd, register const char *device, register u_int unit, return (PCAP_ERROR); } if (ctl.len == -1) { - pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, + snprintf(ebuf, PCAP_ERRBUF_SIZE, "get_dlpi_ppa: hpppa getmsg: control buffer has no data"); return (PCAP_ERROR); } dlp = (dl_hp_ppa_ack_t *)ctl.buf; if (dlp->dl_primitive != DL_HP_PPA_ACK) { - pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, + snprintf(ebuf, PCAP_ERRBUF_SIZE, "get_dlpi_ppa: hpppa unexpected primitive ack 0x%x", (bpf_u_int32)dlp->dl_primitive); return (PCAP_ERROR); } if ((size_t)ctl.len < DL_HP_PPA_ACK_SIZE) { - pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, + snprintf(ebuf, PCAP_ERRBUF_SIZE, "get_dlpi_ppa: hpppa ack too small (%d < %lu)", ctl.len, (unsigned long)DL_HP_PPA_ACK_SIZE); return (PCAP_ERROR); @@ -1688,12 +1718,12 @@ get_dlpi_ppa(register int fd, register const char *device, register u_int unit, return (PCAP_ERROR); } if (ctl.len == -1) { - pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, + snprintf(ebuf, PCAP_ERRBUF_SIZE, "get_dlpi_ppa: hpppa getmsg: control buffer has no data"); return (PCAP_ERROR); } if ((u_int)ctl.len < dlp->dl_length) { - pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, + snprintf(ebuf, PCAP_ERRBUF_SIZE, "get_dlpi_ppa: hpppa ack too small (%d < %lu)", ctl.len, (unsigned long)dlp->dl_length); free(ppa_data_buf); @@ -1750,7 +1780,7 @@ get_dlpi_ppa(register int fd, register const char *device, register u_int unit, * device number of a device with the name "/dev/", * if such a device exists, as the old code did. */ - pcap_snprintf(dname, sizeof(dname), "/dev/%s%u", device, unit); + snprintf(dname, sizeof(dname), "/dev/%s%u", device, unit); if (stat(dname, &statbuf) < 0) { pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, errno, "stat: %s", dname); @@ -1769,12 +1799,12 @@ get_dlpi_ppa(register int fd, register const char *device, register u_int unit, } } if (i == ap->dl_count) { - pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, + snprintf(ebuf, PCAP_ERRBUF_SIZE, "can't find /dev/dlpi PPA for %s%u", device, unit); return (PCAP_ERROR_NO_SUCH_DEVICE); } if (ip->dl_hdw_state == HDW_DEAD) { - pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, + snprintf(ebuf, PCAP_ERRBUF_SIZE, "%s%d: hardware state: DOWN\n", device, unit); free(ppa_data_buf); return (PCAP_ERROR); @@ -1813,13 +1843,13 @@ get_dlpi_ppa(register int fd, register const char *ifname, register u_int unit, if (cp != NULL) ifname = cp + 1; if (nlist(path_vmunix, &nl) < 0) { - pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, "nlist %s failed", + snprintf(ebuf, PCAP_ERRBUF_SIZE, "nlist %s failed", path_vmunix); return (PCAP_ERROR); } if (nl[NL_IFNET].n_value == 0) { - pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, - "could't find %s kernel symbol", + snprintf(ebuf, PCAP_ERRBUF_SIZE, + "couldn't find %s kernel symbol", nl[NL_IFNET].n_name); return (PCAP_ERROR); } @@ -1849,7 +1879,7 @@ get_dlpi_ppa(register int fd, register const char *ifname, register u_int unit, } } - pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, "Can't find %s", ifname); + snprintf(ebuf, PCAP_ERRBUF_SIZE, "Can't find %s", ifname); return (PCAP_ERROR_NO_SUCH_DEVICE); } @@ -1870,7 +1900,7 @@ dlpi_kread(register int fd, register off_t addr, errno, "read"); return (-1); } else if (cc != len) { - pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, "short read (%d != %d)", cc, + snprintf(ebuf, PCAP_ERRBUF_SIZE, "short read (%d != %d)", cc, len); return (-1); } @@ -1886,7 +1916,7 @@ pcap_create_interface(const char *device _U_, char *ebuf) struct pcap_dlpi *pd; #endif - p = pcap_create_common(ebuf, sizeof (struct pcap_dlpi)); + p = PCAP_CREATE_COMMON(ebuf, struct pcap_dlpi); if (p == NULL) return (NULL); diff --git a/pcap-dos.c b/pcap-dos.c index c159b552a95e..897118b1af53 100644 --- a/pcap-dos.c +++ b/pcap-dos.c @@ -12,6 +12,7 @@ #include #include #include +#include /* for INT_MAX */ #include #if defined(USE_32BIT_DRIVERS) @@ -153,7 +154,7 @@ pcap_t *pcap_create_interface (const char *device _U_, char *ebuf) { pcap_t *p; - p = pcap_create_common(ebuf, sizeof (struct pcap_dos)); + p = PCAP_CREATE_COMMON(ebuf, struct pcap_dos); if (p == NULL) return (NULL); @@ -215,7 +216,7 @@ static int pcap_activate_dos (pcap_t *pcap) } else if (stricmp(active_dev->name,pcap->opt.device)) { - pcap_snprintf (pcap->errbuf, PCAP_ERRBUF_SIZE, + snprintf (pcap->errbuf, PCAP_ERRBUF_SIZE, "Cannot use different devices simultaneously " "(`%s' vs. `%s')", active_dev->name, pcap->opt.device); /* XXX - free pcap->buffer? */ @@ -283,7 +284,7 @@ pcap_read_one (pcap_t *p, pcap_handler callback, u_char *data) pcap.len = rx_len; if (callback && - (!p->fcode.bf_insns || bpf_filter(p->fcode.bf_insns, p->buffer, pcap.len, pcap.caplen))) + (!p->fcode.bf_insns || pcap_filter(p->fcode.bf_insns, p->buffer, pcap.len, pcap.caplen))) { filter_count++; @@ -355,7 +356,22 @@ pcap_read_dos (pcap_t *p, int cnt, pcap_handler callback, u_char *data) { int rc, num = 0; - while (num <= cnt || PACKET_COUNT_IS_UNLIMITED(cnt)) + /* + * 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(cnt)) + cnt = INT_MAX; + + while (num <= cnt) { if (p->fd <= 0) return (-1); @@ -539,7 +555,7 @@ int pcap_lookupnet (const char *device, bpf_u_int32 *localnet, net = IN_CLASSC_NET; else { - pcap_snprintf (errbuf, PCAP_ERRBUF_SIZE, "inet class for 0x%lx unknown", mask); + snprintf (errbuf, PCAP_ERRBUF_SIZE, "inet class for 0x%lx unknown", mask); return (-1); } } @@ -553,7 +569,7 @@ int pcap_lookupnet (const char *device, bpf_u_int32 *localnet, /* * Get a list of all interfaces that are present and that we probe okay. * Returns -1 on error, 0 otherwise. - * The list may be NULL epty if no interfaces were up and could be opened. + * The list may be NULL empty if no interfaces were up and could be opened. */ int pcap_platform_finddevs (pcap_if_list_t *devlistp, char *errbuf) { @@ -667,7 +683,7 @@ open_driver (const char *dev_name, char *ebuf, int promisc) if (!(*dev->probe)(dev)) /* call the xx_probe() function */ { - pcap_snprintf (ebuf, PCAP_ERRBUF_SIZE, "failed to detect device `%s'", dev_name); + snprintf (ebuf, PCAP_ERRBUF_SIZE, "failed to detect device `%s'", dev_name); return (NULL); } probed_dev = dev; /* device is probed okay and may be used */ @@ -689,7 +705,7 @@ open_driver (const char *dev_name, char *ebuf, int promisc) if (!(*dev->open)(dev)) { - pcap_snprintf (ebuf, PCAP_ERRBUF_SIZE, "failed to activate device `%s'", dev_name); + snprintf (ebuf, PCAP_ERRBUF_SIZE, "failed to activate device `%s'", dev_name); if (pktInfo.error && !strncmp(dev->name,"pkt",3)) { strcat (ebuf, ": "); @@ -698,7 +714,7 @@ open_driver (const char *dev_name, char *ebuf, int promisc) return (NULL); } - /* Some devices need this to operate in promiscous mode + /* Some devices need this to operate in promiscuous mode */ if (promisc && dev->set_multicast_list) (*dev->set_multicast_list) (dev); @@ -711,14 +727,14 @@ open_driver (const char *dev_name, char *ebuf, int promisc) */ if (!dev) { - pcap_snprintf (ebuf, PCAP_ERRBUF_SIZE, "device `%s' not supported", dev_name); + snprintf (ebuf, PCAP_ERRBUF_SIZE, "device `%s' not supported", dev_name); return (NULL); } not_probed: if (!probed_dev) { - pcap_snprintf (ebuf, PCAP_ERRBUF_SIZE, "device `%s' not probed", dev_name); + snprintf (ebuf, PCAP_ERRBUF_SIZE, "device `%s' not probed", dev_name); return (NULL); } return (dev); @@ -943,7 +959,7 @@ static void pcap_init_hook (void) } /* - * Supress PRINT message from Watt-32's sock_init() + * Suppress PRINT message from Watt-32's sock_init() */ static void null_print (void) {} @@ -1005,7 +1021,7 @@ static int init_watt32 (struct pcap *pcap, const char *dev_name, char *err_buf) } else if (rc && using_pktdrv) { - pcap_snprintf (err_buf, PCAP_ERRBUF_SIZE, "sock_init() failed, code %d", rc); + snprintf (err_buf, PCAP_ERRBUF_SIZE, "sock_init() failed, code %d", rc); return (0); } @@ -1031,11 +1047,9 @@ static int init_watt32 (struct pcap *pcap, const char *dev_name, char *err_buf) pcap_save.linktype = _eth_get_hwtype (NULL, NULL); pcap_save.snapshot = MTU > 0 ? MTU : ETH_MAX; /* assume 1514 */ -#if 1 /* prevent use of resolve() and resolve_ip() */ last_nameserver = 0; -#endif return (1); } @@ -1190,14 +1204,14 @@ static void ndis_close (struct device *dev) static int ndis_open (struct device *dev) { - int promis = (dev->flags & IFF_PROMISC); + int promisc = (dev->flags & IFF_PROMISC); #ifdef USE_NDIS2 - if (!NdisInit(promis)) + if (!NdisInit(promisc)) return (0); return (1); #else - ARGSUSED (promis); + ARGSUSED (promisc); return (0); #endif } diff --git a/pcap-dpdk.c b/pcap-dpdk.c new file mode 100644 index 000000000000..025a67482c0f --- /dev/null +++ b/pcap-dpdk.c @@ -0,0 +1,1086 @@ +/* + * Copyright (C) 2018 jingle YANG. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* +Date: Dec 16, 2018 + +Description: +1. Pcap-dpdk provides libpcap the ability to use DPDK with the device name as dpdk:{portid}, such as dpdk:0. +2. DPDK is a set of libraries and drivers for fast packet processing. (https://www.dpdk.org/) +3. The testprogs/capturetest provides 6.4Gbps/800,000 pps on Intel 10-Gigabit X540-AT2 with DPDK 18.11. + +Limitations: +1. DPDK support will be on if DPDK is available. Please set DIR for --with-dpdk[=DIR] with ./configure or -DDPDK_DIR[=DIR] with cmake if DPDK is installed manually. +2. Only support link libdpdk.so dynamically, because the libdpdk.a will not work correctly. +3. Only support read operation, and packet injection has not been supported yet. + +Usage: +1. Compile DPDK as shared library and install.(https://github.com/DPDK/dpdk.git) + +You shall modify the file $RTE_SDK/$RTE_TARGET/.config and set: +CONFIG_RTE_BUILD_SHARED_LIB=y +By the following command: +sed -i 's/CONFIG_RTE_BUILD_SHARED_LIB=n/CONFIG_RTE_BUILD_SHARED_LIB=y/' $RTE_SDK/$RTE_TARGET/.config + +2. Launch l2fwd that is one of DPDK examples correctly, and get device information. + +You shall learn how to bind nic with DPDK-compatible driver by $RTE_SDK/usertools/dpdk-devbind.py, such as igb_uio. +And enable hugepages by dpdk-setup.sh + +Then launch the l2fwd with dynamic driver support. For example: +$RTE_SDK/examples/l2fwd/$RTE_TARGET/l2fwd -dlibrte_pmd_e1000.so -dlibrte_pmd_ixgbe.so -dlibrte_mempool_ring.so -- -p 0x1 + +3. Compile libpcap with dpdk options. + +If DPDK has not been found automatically, you shall export DPDK environment variable which are used for compiling DPDK. And then pass $RTE_SDK/$RTE_TARGET to --with-dpdk or -DDPDK_DIR + +export RTE_SDK={your DPDK base directory} +export RTE_TARGET={your target name} + +3.1 With configure + +./configure --with-dpdk=$RTE_SDK/$RTE_TARGET && make -s all && make -s testprogs && make install + +3.2 With cmake + +mkdir -p build && cd build && cmake -DDPDK_DIR=$RTE_SDK/$RTE_TARGET ../ && make -s all && make -s testprogs && make install + +4. Link your own program with libpcap, and use DPDK with the device name as dpdk:{portid}, such as dpdk:0. +And you shall set DPDK configure options by environment variable DPDK_CFG +For example, the testprogs/capturetest could be lanched by: + +env DPDK_CFG="--log-level=debug -l0 -dlibrte_pmd_e1000.so -dlibrte_pmd_ixgbe.so -dlibrte_mempool_ring.so" ./capturetest -i dpdk:0 +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include /* for INT_MAX */ +#include + +#include + +//header for calling dpdk +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcap-int.h" +#include "pcap-dpdk.h" + +/* + * Deal with API changes that break source compatibility. + */ + +#ifdef HAVE_STRUCT_RTE_ETHER_ADDR +#define ETHER_ADDR_TYPE struct rte_ether_addr +#else +#define ETHER_ADDR_TYPE struct ether_addr +#endif + +#define DPDK_DEF_LOG_LEV RTE_LOG_ERR +// +// This is set to 0 if we haven't initialized DPDK yet, 1 if we've +// successfully initialized it, a negative value, which is the negative +// of the rte_errno from rte_eal_init(), if we tried to initialize it +// and got an error. +// +static int is_dpdk_pre_inited=0; +#define DPDK_LIB_NAME "libpcap_dpdk" +#define DPDK_DESC "Data Plane Development Kit (DPDK) Interface" +#define DPDK_ERR_PERM_MSG "permission denied, DPDK needs root permission" +#define DPDK_ARGC_MAX 64 +#define DPDK_CFG_MAX_LEN 1024 +#define DPDK_DEV_NAME_MAX 32 +#define DPDK_DEV_DESC_MAX 512 +#define DPDK_CFG_ENV_NAME "DPDK_CFG" +#define DPDK_DEF_MIN_SLEEP_MS 1 +static char dpdk_cfg_buf[DPDK_CFG_MAX_LEN]; +#define DPDK_MAC_ADDR_SIZE 32 +#define DPDK_DEF_MAC_ADDR "00:00:00:00:00:00" +#define DPDK_PCI_ADDR_SIZE 16 +#define DPDK_DEF_CFG "--log-level=error -l0 -dlibrte_pmd_e1000.so -dlibrte_pmd_ixgbe.so -dlibrte_mempool_ring.so" +#define DPDK_PREFIX "dpdk:" +#define DPDK_PORTID_MAX 65535U +#define MBUF_POOL_NAME "mbuf_pool" +#define DPDK_TX_BUF_NAME "tx_buffer" +//The number of elements in the mbuf pool. +#define DPDK_NB_MBUFS 8192U +#define MEMPOOL_CACHE_SIZE 256 +#define MAX_PKT_BURST 32 +// Configurable number of RX/TX ring descriptors +#define RTE_TEST_RX_DESC_DEFAULT 1024 +#define RTE_TEST_TX_DESC_DEFAULT 1024 + +static uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT; +static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT; + +#ifdef RTE_ETHER_MAX_JUMBO_FRAME_LEN +#define RTE_ETH_PCAP_SNAPLEN RTE_ETHER_MAX_JUMBO_FRAME_LEN +#else +#define RTE_ETH_PCAP_SNAPLEN ETHER_MAX_JUMBO_FRAME_LEN +#endif + +static struct rte_eth_dev_tx_buffer *tx_buffer; + +struct dpdk_ts_helper{ + struct timeval start_time; + uint64_t start_cycles; + uint64_t hz; +}; +struct pcap_dpdk{ + pcap_t * orig; + uint16_t portid; // portid of DPDK + int must_clear_promisc; + uint64_t bpf_drop; + int nonblock; + struct timeval required_select_timeout; + struct timeval prev_ts; + struct rte_eth_stats prev_stats; + struct timeval curr_ts; + struct rte_eth_stats curr_stats; + uint64_t pps; + uint64_t bps; + struct rte_mempool * pktmbuf_pool; + struct dpdk_ts_helper ts_helper; + ETHER_ADDR_TYPE eth_addr; + char mac_addr[DPDK_MAC_ADDR_SIZE]; + char pci_addr[DPDK_PCI_ADDR_SIZE]; + unsigned char pcap_tmp_buf[RTE_ETH_PCAP_SNAPLEN]; +}; + +static struct rte_eth_conf port_conf = { + .rxmode = { + .split_hdr_size = 0, + }, + .txmode = { + .mq_mode = ETH_MQ_TX_NONE, + }, +}; + +static void dpdk_fmt_errmsg_for_rte_errno(char *, size_t, int, + PCAP_FORMAT_STRING(const char *), ...) PCAP_PRINTFLIKE(4, 5); + +/* + * Generate an error message based on a format, arguments, and an + * rte_errno, with a message for the rte_errno after the formatted output. + */ +static void dpdk_fmt_errmsg_for_rte_errno(char *errbuf, size_t errbuflen, + int errnum, const char *fmt, ...) +{ + va_list ap; + size_t msglen; + char *p; + size_t errbuflen_remaining; + + va_start(ap, fmt); + vsnprintf(errbuf, errbuflen, fmt, ap); + va_end(ap); + msglen = strlen(errbuf); + + /* + * Do we have enough space to append ": "? + * Including the terminating '\0', that's 3 bytes. + */ + if (msglen + 3 > errbuflen) { + /* No - just give them what we've produced. */ + return; + } + p = errbuf + msglen; + errbuflen_remaining = errbuflen - msglen; + *p++ = ':'; + *p++ = ' '; + *p = '\0'; + msglen += 2; + errbuflen_remaining -= 2; + + /* + * Now append the string for the error code. + * rte_strerror() is thread-safe, at least as of dpdk 18.11, + * unlike strerror() - it uses strerror_r() rather than strerror() + * for UN*X errno values, and prints to what I assume is a per-thread + * buffer (based on the "PER_LCORE" in "RTE_DEFINE_PER_LCORE" used + * to declare the buffers statically) for DPDK errors. + */ + snprintf(p, errbuflen_remaining, "%s", rte_strerror(errnum)); +} + +static int dpdk_init_timer(struct pcap_dpdk *pd){ + gettimeofday(&(pd->ts_helper.start_time),NULL); + pd->ts_helper.start_cycles = rte_get_timer_cycles(); + pd->ts_helper.hz = rte_get_timer_hz(); + if (pd->ts_helper.hz == 0){ + return -1; + } + return 0; +} +static inline void calculate_timestamp(struct dpdk_ts_helper *helper,struct timeval *ts) +{ + uint64_t cycles; + // delta + struct timeval cur_time; + cycles = rte_get_timer_cycles() - helper->start_cycles; + cur_time.tv_sec = (time_t)(cycles/helper->hz); + cur_time.tv_usec = (suseconds_t)((cycles%helper->hz)*1e6/helper->hz); + timeradd(&(helper->start_time), &cur_time, ts); +} + +static uint32_t dpdk_gather_data(unsigned char *data, uint32_t len, struct rte_mbuf *mbuf) +{ + uint32_t total_len = 0; + while (mbuf && (total_len+mbuf->data_len) < len ){ + rte_memcpy(data+total_len, rte_pktmbuf_mtod(mbuf,void *),mbuf->data_len); + total_len+=mbuf->data_len; + mbuf=mbuf->next; + } + return total_len; +} + + +static int dpdk_read_with_timeout(pcap_t *p, struct rte_mbuf **pkts_burst, const uint16_t burst_cnt){ + struct pcap_dpdk *pd = (struct pcap_dpdk*)(p->priv); + int nb_rx = 0; + int timeout_ms = p->opt.timeout; + int sleep_ms = 0; + if (pd->nonblock){ + // In non-blocking mode, just read once, no matter how many packets are captured. + nb_rx = (int)rte_eth_rx_burst(pd->portid, 0, pkts_burst, burst_cnt); + }else{ + // In blocking mode, read many times until packets are captured or timeout or break_loop is set. + // if timeout_ms == 0, it may be blocked forever. + while (timeout_ms == 0 || sleep_ms < timeout_ms){ + nb_rx = (int)rte_eth_rx_burst(pd->portid, 0, pkts_burst, burst_cnt); + if (nb_rx){ // got packets within timeout_ms + break; + }else{ // no packet arrives at this round. + if (p->break_loop){ + break; + } + // sleep for a very short while. + // block sleep is the only choice, since usleep() will impact performance dramatically. + rte_delay_us_block(DPDK_DEF_MIN_SLEEP_MS*1000); + sleep_ms += DPDK_DEF_MIN_SLEEP_MS; + } + } + } + return nb_rx; +} + +static int pcap_dpdk_dispatch(pcap_t *p, int max_cnt, pcap_handler cb, u_char *cb_arg) +{ + struct pcap_dpdk *pd = (struct pcap_dpdk*)(p->priv); + int burst_cnt = 0; + int nb_rx = 0; + struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; + struct rte_mbuf *m; + struct pcap_pkthdr pcap_header; + // In DPDK, pkt_len is sum of lengths for all segments. And data_len is for one segment + uint32_t pkt_len = 0; + uint32_t caplen = 0; + u_char *bp = NULL; + int i=0; + unsigned int gather_len =0; + int pkt_cnt = 0; + u_char *large_buffer=NULL; + int timeout_ms = p->opt.timeout; + + /* + * 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_cnt)) + max_cnt = INT_MAX; + + if (max_cnt < MAX_PKT_BURST){ + burst_cnt = max_cnt; + }else{ + burst_cnt = MAX_PKT_BURST; + } + + while( pkt_cnt < max_cnt){ + if (p->break_loop){ + p->break_loop = 0; + return PCAP_ERROR_BREAK; + } + // read once in non-blocking mode, or try many times waiting for timeout_ms. + // if timeout_ms == 0, it will be blocked until one packet arrives or break_loop is set. + nb_rx = dpdk_read_with_timeout(p, pkts_burst, burst_cnt); + if (nb_rx == 0){ + if (pd->nonblock){ + RTE_LOG(DEBUG, USER1, "dpdk: no packets available in non-blocking mode.\n"); + }else{ + if (p->break_loop){ + RTE_LOG(DEBUG, USER1, "dpdk: no packets available and break_loop is set in blocking mode.\n"); + p->break_loop = 0; + return PCAP_ERROR_BREAK; + + } + RTE_LOG(DEBUG, USER1, "dpdk: no packets available for timeout %d ms in blocking mode.\n", timeout_ms); + } + // break if dpdk reads 0 packet, no matter in blocking(timeout) or non-blocking mode. + break; + } + pkt_cnt += nb_rx; + for ( i = 0; i < nb_rx; i++) { + m = pkts_burst[i]; + calculate_timestamp(&(pd->ts_helper),&(pcap_header.ts)); + pkt_len = rte_pktmbuf_pkt_len(m); + // caplen = min(pkt_len, p->snapshot); + // caplen will not be changed, no matter how long the rte_pktmbuf + caplen = pkt_len < (uint32_t)p->snapshot ? pkt_len: (uint32_t)p->snapshot; + pcap_header.caplen = caplen; + pcap_header.len = pkt_len; + // volatile prefetch + rte_prefetch0(rte_pktmbuf_mtod(m, void *)); + bp = NULL; + if (m->nb_segs == 1) + { + bp = rte_pktmbuf_mtod(m, u_char *); + }else{ + // use fast buffer pcap_tmp_buf if pkt_len is small, no need to call malloc and free + if ( pkt_len <= RTE_ETH_PCAP_SNAPLEN) + { + gather_len = dpdk_gather_data(pd->pcap_tmp_buf, RTE_ETH_PCAP_SNAPLEN, m); + bp = pd->pcap_tmp_buf; + }else{ + // need call free later + large_buffer = (u_char *)malloc(caplen*sizeof(u_char)); + gather_len = dpdk_gather_data(large_buffer, caplen, m); + bp = large_buffer; + } + + } + if (bp){ + if (p->fcode.bf_insns==NULL || pcap_filter(p->fcode.bf_insns, bp, pcap_header.len, pcap_header.caplen)){ + cb(cb_arg, &pcap_header, bp); + }else{ + pd->bpf_drop++; + } + } + //free all pktmbuf + rte_pktmbuf_free(m); + if (large_buffer){ + free(large_buffer); + large_buffer=NULL; + } + } + } + return pkt_cnt; +} + +static int pcap_dpdk_inject(pcap_t *p, const void *buf _U_, int size _U_) +{ + //not implemented yet + pcap_strlcpy(p->errbuf, + "dpdk error: Inject function has not been implemented yet", + PCAP_ERRBUF_SIZE); + return PCAP_ERROR; +} + +static void pcap_dpdk_close(pcap_t *p) +{ + struct pcap_dpdk *pd = p->priv; + if (pd==NULL) + { + return; + } + if (pd->must_clear_promisc) + { + rte_eth_promiscuous_disable(pd->portid); + } + rte_eth_dev_stop(pd->portid); + rte_eth_dev_close(pd->portid); + pcap_cleanup_live_common(p); +} + +static void nic_stats_display(struct pcap_dpdk *pd) +{ + uint16_t portid = pd->portid; + struct rte_eth_stats stats; + rte_eth_stats_get(portid, &stats); + RTE_LOG(INFO,USER1, "portid:%d, RX-packets: %-10"PRIu64" RX-errors: %-10"PRIu64 + " RX-bytes: %-10"PRIu64" RX-Imissed: %-10"PRIu64"\n", portid, stats.ipackets, stats.ierrors, + stats.ibytes,stats.imissed); + RTE_LOG(INFO,USER1, "portid:%d, RX-PPS: %-10"PRIu64" RX-Mbps: %.2lf\n", portid, pd->pps, pd->bps/1e6f ); +} + +static int pcap_dpdk_stats(pcap_t *p, struct pcap_stat *ps) +{ + struct pcap_dpdk *pd = p->priv; + calculate_timestamp(&(pd->ts_helper), &(pd->curr_ts)); + rte_eth_stats_get(pd->portid,&(pd->curr_stats)); + if (ps){ + ps->ps_recv = pd->curr_stats.ipackets; + ps->ps_drop = pd->curr_stats.ierrors; + ps->ps_drop += pd->bpf_drop; + ps->ps_ifdrop = pd->curr_stats.imissed; + } + uint64_t delta_pkt = pd->curr_stats.ipackets - pd->prev_stats.ipackets; + struct timeval delta_tm; + timersub(&(pd->curr_ts),&(pd->prev_ts), &delta_tm); + uint64_t delta_usec = delta_tm.tv_sec*1e6+delta_tm.tv_usec; + uint64_t delta_bit = (pd->curr_stats.ibytes-pd->prev_stats.ibytes)*8; + RTE_LOG(DEBUG, USER1, "delta_usec: %-10"PRIu64" delta_pkt: %-10"PRIu64" delta_bit: %-10"PRIu64"\n", delta_usec, delta_pkt, delta_bit); + pd->pps = (uint64_t)(delta_pkt*1e6f/delta_usec); + pd->bps = (uint64_t)(delta_bit*1e6f/delta_usec); + nic_stats_display(pd); + pd->prev_stats = pd->curr_stats; + pd->prev_ts = pd->curr_ts; + return 0; +} + +static int pcap_dpdk_setnonblock(pcap_t *p, int nonblock){ + struct pcap_dpdk *pd = (struct pcap_dpdk*)(p->priv); + pd->nonblock = nonblock; + return 0; +} + +static int pcap_dpdk_getnonblock(pcap_t *p){ + struct pcap_dpdk *pd = (struct pcap_dpdk*)(p->priv); + return pd->nonblock; +} +static int check_link_status(uint16_t portid, struct rte_eth_link *plink) +{ + // wait up to 9 seconds to get link status + rte_eth_link_get(portid, plink); + return plink->link_status == ETH_LINK_UP; +} +static void eth_addr_str(ETHER_ADDR_TYPE *addrp, char* mac_str, int len) +{ + int offset=0; + if (addrp == NULL){ + snprintf(mac_str, len-1, DPDK_DEF_MAC_ADDR); + return; + } + for (int i=0; i<6; i++) + { + if (offset >= len) + { // buffer overflow + return; + } + if (i==0) + { + snprintf(mac_str+offset, len-1-offset, "%02X",addrp->addr_bytes[i]); + offset+=2; // FF + }else{ + snprintf(mac_str+offset, len-1-offset, ":%02X", addrp->addr_bytes[i]); + offset+=3; // :FF + } + } + return; +} +// return portid by device name, otherwise return -1 +static uint16_t portid_by_device(char * device) +{ + uint16_t ret = DPDK_PORTID_MAX; + int len = strlen(device); + int prefix_len = strlen(DPDK_PREFIX); + unsigned long ret_ul = 0L; + char *pEnd; + if (len<=prefix_len || strncmp(device, DPDK_PREFIX, prefix_len)) // check prefix dpdk: + { + return ret; + } + //check all chars are digital + for (int i=prefix_len; device[i]; i++){ + if (device[i]<'0' || device[i]>'9'){ + return ret; + } + } + ret_ul = strtoul(&(device[prefix_len]), &pEnd, 10); + if (pEnd == &(device[prefix_len]) || *pEnd != '\0'){ + return ret; + } + // too large for portid + if (ret_ul >= DPDK_PORTID_MAX){ + return ret; + } + ret = (uint16_t)ret_ul; + return ret; +} + +static int parse_dpdk_cfg(char* dpdk_cfg,char** dargv) +{ + int cnt=0; + memset(dargv,0,sizeof(dargv[0])*DPDK_ARGC_MAX); + //current process name + int skip_space = 1; + int i=0; + RTE_LOG(INFO, USER1,"dpdk cfg: %s\n",dpdk_cfg); + // find first non space char + // The last opt is NULL + for (i=0;dpdk_cfg[i] && cntpriv; + pd->orig = p; + int ret = PCAP_ERROR; + uint16_t nb_ports=0; + uint16_t portid= DPDK_PORTID_MAX; + unsigned nb_mbufs = DPDK_NB_MBUFS; + struct rte_eth_rxconf rxq_conf; + struct rte_eth_txconf txq_conf; + struct rte_eth_conf local_port_conf = port_conf; + struct rte_eth_dev_info dev_info; + int is_port_up = 0; + struct rte_eth_link link; + do{ + //init EAL; fail if we have insufficient permission + char dpdk_pre_init_errbuf[PCAP_ERRBUF_SIZE]; + ret = dpdk_pre_init(dpdk_pre_init_errbuf, 0); + if (ret < 0) + { + // This returns a negative value on an error. + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Can't open device %s: %s", + p->opt.device, dpdk_pre_init_errbuf); + // ret is set to the correct error + break; + } + if (ret == 0) + { + // This means DPDK isn't available on this machine. + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Can't open device %s: DPDK is not available on this machine", + p->opt.device); + return PCAP_ERROR_NO_SUCH_DEVICE; + } + + ret = dpdk_init_timer(pd); + if (ret<0) + { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "dpdk error: Init timer is zero with device %s", + p->opt.device); + ret = PCAP_ERROR; + break; + } + + nb_ports = rte_eth_dev_count_avail(); + if (nb_ports == 0) + { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "dpdk error: No Ethernet ports"); + ret = PCAP_ERROR; + break; + } + + portid = portid_by_device(p->opt.device); + if (portid == DPDK_PORTID_MAX){ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "dpdk error: portid is invalid. device %s", + p->opt.device); + ret = PCAP_ERROR_NO_SUCH_DEVICE; + break; + } + + pd->portid = portid; + + if (p->snapshot <= 0 || p->snapshot > MAXIMUM_SNAPLEN) + { + p->snapshot = MAXIMUM_SNAPLEN; + } + // create the mbuf pool + pd->pktmbuf_pool = rte_pktmbuf_pool_create(MBUF_POOL_NAME, nb_mbufs, + MEMPOOL_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, + rte_socket_id()); + if (pd->pktmbuf_pool == NULL) + { + dpdk_fmt_errmsg_for_rte_errno(p->errbuf, + PCAP_ERRBUF_SIZE, rte_errno, + "dpdk error: Cannot init mbuf pool"); + ret = PCAP_ERROR; + break; + } + // config dev + rte_eth_dev_info_get(portid, &dev_info); + if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE) + { + local_port_conf.txmode.offloads |=DEV_TX_OFFLOAD_MBUF_FAST_FREE; + } + // only support 1 queue + ret = rte_eth_dev_configure(portid, 1, 1, &local_port_conf); + if (ret < 0) + { + dpdk_fmt_errmsg_for_rte_errno(p->errbuf, + PCAP_ERRBUF_SIZE, -ret, + "dpdk error: Cannot configure device: port=%u", + portid); + ret = PCAP_ERROR; + break; + } + // adjust rx tx + ret = rte_eth_dev_adjust_nb_rx_tx_desc(portid, &nb_rxd, &nb_txd); + if (ret < 0) + { + dpdk_fmt_errmsg_for_rte_errno(p->errbuf, + PCAP_ERRBUF_SIZE, -ret, + "dpdk error: Cannot adjust number of descriptors: port=%u", + portid); + ret = PCAP_ERROR; + break; + } + // get MAC addr + rte_eth_macaddr_get(portid, &(pd->eth_addr)); + eth_addr_str(&(pd->eth_addr), pd->mac_addr, DPDK_MAC_ADDR_SIZE-1); + + // init one RX queue + rxq_conf = dev_info.default_rxconf; + rxq_conf.offloads = local_port_conf.rxmode.offloads; + ret = rte_eth_rx_queue_setup(portid, 0, nb_rxd, + rte_eth_dev_socket_id(portid), + &rxq_conf, + pd->pktmbuf_pool); + if (ret < 0) + { + dpdk_fmt_errmsg_for_rte_errno(p->errbuf, + PCAP_ERRBUF_SIZE, -ret, + "dpdk error: rte_eth_rx_queue_setup:port=%u", + portid); + ret = PCAP_ERROR; + break; + } + + // init one TX queue + txq_conf = dev_info.default_txconf; + txq_conf.offloads = local_port_conf.txmode.offloads; + ret = rte_eth_tx_queue_setup(portid, 0, nb_txd, + rte_eth_dev_socket_id(portid), + &txq_conf); + if (ret < 0) + { + dpdk_fmt_errmsg_for_rte_errno(p->errbuf, + PCAP_ERRBUF_SIZE, -ret, + "dpdk error: rte_eth_tx_queue_setup:port=%u", + portid); + ret = PCAP_ERROR; + break; + } + // Initialize TX buffers + tx_buffer = rte_zmalloc_socket(DPDK_TX_BUF_NAME, + RTE_ETH_TX_BUFFER_SIZE(MAX_PKT_BURST), 0, + rte_eth_dev_socket_id(portid)); + if (tx_buffer == NULL) + { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "dpdk error: Cannot allocate buffer for tx on port %u", portid); + ret = PCAP_ERROR; + break; + } + rte_eth_tx_buffer_init(tx_buffer, MAX_PKT_BURST); + // Start device + ret = rte_eth_dev_start(portid); + if (ret < 0) + { + dpdk_fmt_errmsg_for_rte_errno(p->errbuf, + PCAP_ERRBUF_SIZE, -ret, + "dpdk error: rte_eth_dev_start:port=%u", + portid); + ret = PCAP_ERROR; + break; + } + // set promiscuous mode + if (p->opt.promisc){ + pd->must_clear_promisc=1; + rte_eth_promiscuous_enable(portid); + } + // check link status + is_port_up = check_link_status(portid, &link); + if (!is_port_up){ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "dpdk error: link is down, port=%u",portid); + ret = PCAP_ERROR_IFACE_NOT_UP; + break; + } + // reset statistics + rte_eth_stats_reset(pd->portid); + calculate_timestamp(&(pd->ts_helper), &(pd->prev_ts)); + rte_eth_stats_get(pd->portid,&(pd->prev_stats)); + // format pcap_t + pd->portid = portid; + p->fd = pd->portid; + if (p->snapshot <=0 || p->snapshot> MAXIMUM_SNAPLEN) + { + p->snapshot = MAXIMUM_SNAPLEN; + } + p->linktype = DLT_EN10MB; // Ethernet, the 10MB is historical. + p->selectable_fd = p->fd; + p->read_op = pcap_dpdk_dispatch; + p->inject_op = pcap_dpdk_inject; + // using pcap_filter currently, though DPDK provides their own BPF function. Because DPDK BPF needs load a ELF file as a filter. + p->setfilter_op = install_bpf_program; + p->setdirection_op = NULL; + p->set_datalink_op = NULL; + p->getnonblock_op = pcap_dpdk_getnonblock; + p->setnonblock_op = pcap_dpdk_setnonblock; + p->stats_op = pcap_dpdk_stats; + p->cleanup_op = pcap_dpdk_close; + p->breakloop_op = pcap_breakloop_common; + // set default timeout + pd->required_select_timeout.tv_sec = 0; + pd->required_select_timeout.tv_usec = DPDK_DEF_MIN_SLEEP_MS*1000; + p->required_select_timeout = &pd->required_select_timeout; + ret = 0; // OK + }while(0); + + if (ret <= PCAP_ERROR) // all kinds of error code + { + pcap_cleanup_live_common(p); + }else{ + rte_eth_dev_get_name_by_port(portid,pd->pci_addr); + RTE_LOG(INFO, USER1,"Port %d device: %s, MAC:%s, PCI:%s\n", portid, p->opt.device, pd->mac_addr, pd->pci_addr); + RTE_LOG(INFO, USER1,"Port %d Link Up. Speed %u Mbps - %s\n", + portid, link.link_speed, + (link.link_duplex == ETH_LINK_FULL_DUPLEX) ? + ("full-duplex") : ("half-duplex\n")); + } + return ret; +} + +// device name for dpdk should be in the form as dpdk:number, such as dpdk:0 +pcap_t * pcap_dpdk_create(const char *device, char *ebuf, int *is_ours) +{ + pcap_t *p=NULL; + *is_ours = 0; + + *is_ours = !strncmp(device, "dpdk:", 5); + if (! *is_ours) + return NULL; + //memset will happen + p = PCAP_CREATE_COMMON(ebuf, struct pcap_dpdk); + + if (p == NULL) + return NULL; + p->activate_op = pcap_dpdk_activate; + return p; +} + +int pcap_dpdk_findalldevs(pcap_if_list_t *devlistp, char *ebuf) +{ + int ret=0; + unsigned int nb_ports = 0; + char dpdk_name[DPDK_DEV_NAME_MAX]; + char dpdk_desc[DPDK_DEV_DESC_MAX]; + ETHER_ADDR_TYPE eth_addr; + char mac_addr[DPDK_MAC_ADDR_SIZE]; + char pci_addr[DPDK_PCI_ADDR_SIZE]; + do{ + // init EAL; return "DPDK not available" if we + // have insufficient permission + char dpdk_pre_init_errbuf[PCAP_ERRBUF_SIZE]; + ret = dpdk_pre_init(dpdk_pre_init_errbuf, 1); + if (ret < 0) + { + // This returns a negative value on an error. + snprintf(ebuf, PCAP_ERRBUF_SIZE, + "Can't look for DPDK devices: %s", + dpdk_pre_init_errbuf); + ret = PCAP_ERROR; + break; + } + if (ret == 0) + { + // This means DPDK isn't available on this machine. + // That just means "don't return any devices". + break; + } + nb_ports = rte_eth_dev_count_avail(); + if (nb_ports == 0) + { + // That just means "don't return any devices". + ret = 0; + break; + } + for (unsigned int i=0; itap.th_wirelen > snaplen ? snaplen : ph->tap .th_wirelen ; - if (bpf_filter(fcode, (char *)ph->packet, + if (pcap_filter(fcode, (char *)ph->packet, ph->tap.th_wirelen, caplen)) { if (cnt >= 0 && --cnt < 0) goto out; @@ -89,7 +89,7 @@ readloop(int cnt, int if_fd, struct bpf_program *fp, printfunc printit) } #else /* !IBMRTPC */ caplen = cc > snaplen ? snaplen : cc ; - if (bpf_filter(fcode, buf.hdr.packet, cc, caplen)) { + if (pcap_filter(fcode, buf.hdr.packet, cc, caplen)) { if (cnt >= 0 && --cnt < 0) goto out; (*printit)(buf.hdr.packet, &tv, cc, caplen); diff --git a/pcap-filter.manmisc.in b/pcap-filter.manmisc.in index 777e7350cf51..4b44cee9c513 100644 --- a/pcap-filter.manmisc.in +++ b/pcap-filter.manmisc.in @@ -18,22 +18,22 @@ .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" -.TH PCAP-FILTER @MAN_MISC_INFO@ "5 November 2017" +.TH PCAP-FILTER @MAN_MISC_INFO@ "19 November 2022" .SH NAME pcap-filter \- packet filter syntax .br .ad .SH DESCRIPTION .LP -.B pcap_compile() +.BR pcap_compile (3PCAP) is used to compile a string into a filter program. The resulting filter program can then be applied to some stream of packets to determine which packets will be supplied to -.BR pcap_loop(3PCAP) , -.BR pcap_dispatch(3PCAP) , -.BR pcap_next(3PCAP) , +.BR pcap_loop (3PCAP), +.BR pcap_dispatch (3PCAP), +.BR pcap_next (3PCAP), or -.BR pcap_next_ex(3PCAP) . +.BR pcap_next_ex (3PCAP). .LP The \fIfilter expression\fP consists of one or more .IR primitives . @@ -47,11 +47,11 @@ different kinds of qualifier: qualifiers say what kind of thing the id name or number refers to. Possible types are .BR host , -.B net , +.BR net , .B port and .BR portrange . -E.g., `host foo', `net 128.3', `port 20', `portrange 6000-6008'. +E.g., `\fBhost\fP foo', `\fBnet\fP 128.3', `\fBport\fP 20', `\fBportrange\fP 6000-6008'. If there is no type qualifier, .B host @@ -72,11 +72,9 @@ Possible directions are .BR addr3 , and .BR addr4 . -E.g., `src foo', `dst net 128.3', `src or dst port ftp-data'. +E.g., `\fBsrc\fP foo', `\fBdst net\fP 128.3', `\fBsrc or dst port\fP ftp-data'. If -there is no dir qualifier, -.B "src or dst" -is assumed. +there is no dir qualifier, `\fBsrc or dst\fP' is assumed. The .BR ra , .BR ta , @@ -90,7 +88,7 @@ qualifiers are only valid for IEEE 802.11 Wireless LAN link layers. .I proto qualifiers restrict the match to a particular protocol. Possible -protos are: +protocols are: .BR ether , .BR fddi , .BR tr , @@ -100,28 +98,30 @@ protos are: .BR arp , .BR rarp , .BR decnet , +.BR sctp , .B tcp and .BR udp . -E.g., `ether src foo', `arp net 128.3', `tcp port 21', `udp portrange -7000-7009', `wlan addr2 0:2:3:4:5:6'. -If there is -no proto qualifier, all protocols consistent with the type are -assumed. -E.g., `src foo' means `(ip or arp or rarp) src foo' -(except the latter is not legal syntax), `net bar' means `(ip or -arp or rarp) net bar' and `port 53' means `(tcp or udp) port 53'. +E.g., `\fBether src\fP foo', `\fBarp net\fP 128.3', `\fBtcp port\fP 21', +`\fBudp portrange\fP 7000-7009', `\fBwlan addr2\fP 0:2:3:4:5:6'. +If there is no +.I proto +qualifier, all protocols consistent with the type are assumed. +E.g., `\fBsrc\fP foo' means `\fB(ip or arp or rarp) src\fP foo', +`\fBnet\fP bar' means `\fB(ip or arp or rarp) net\fP bar' and +`\fBport\fP 53' means `\fB(tcp or udp or sctp) port\fP 53' +(note that these examples use invalid syntax to illustrate the principle). .LP -[`fddi' is actually an alias for `ether'; the parser treats them +[\fBfddi\fP is actually an alias for \fBether\fP; the parser treats them identically as meaning ``the data link level used on the specified -network interface.'' FDDI headers contain Ethernet-like source +network interface''. FDDI headers contain Ethernet-like source and destination addresses, and often contain Ethernet-like packet types, so you can filter on these FDDI fields just as with the analogous Ethernet fields. FDDI headers also contain other fields, but you cannot name them explicitly in a filter expression. .LP -Similarly, `tr' and `wlan' are aliases for `ether'; the previous +Similarly, \fBtr\fP and \fBwlan\fP are aliases for \fBether\fP; the previous paragraph's statements about FDDI headers also apply to Token Ring and 802.11 wireless LAN headers. For 802.11 headers, the destination address is the DA field and the source address is the SA field; the @@ -141,48 +141,49 @@ More complex filter expressions are built up by using the words .B or and .B not +(or equivalently: `\fB&&\fP', `\fB||\fP' and `\fB!\fP' respectively) to combine primitives. -E.g., `host foo and not port ftp and not port ftp-data'. +E.g., `\fBhost\fP foo \fBand not port\fP ftp \fBand not port\fP ftp-data'. To save typing, identical qualifier lists can be omitted. E.g., -`tcp dst port ftp or ftp-data or domain' is exactly the same as -`tcp dst port ftp or tcp dst port ftp-data or tcp dst port domain'. +`\fBtcp dst port\fP ftp \fBor\fP ftp-data \fBor\fP domain' is exactly the same as +`\fBtcp dst port\fP ftp \fBor tcp dst port\fP ftp-data \fBor tcp dst port\fP domain'. .LP Allowable primitives are: -.IP "\fBdst host \fIhost\fR" -True if the IPv4/v6 destination field of the packet is \fIhost\fP, +.IP "\fBdst host \fIhostnameaddr\fR" +True if the IPv4/v6 destination field of the packet is \fIhostnameaddr\fP, which may be either an address or a name. -.IP "\fBsrc host \fIhost\fR" -True if the IPv4/v6 source field of the packet is \fIhost\fP. -.IP "\fBhost \fIhost\fP" -True if either the IPv4/v6 source or destination of the packet is \fIhost\fP. +.IP "\fBsrc host \fIhostnameaddr\fR" +True if the IPv4/v6 source field of the packet is \fIhostnameaddr\fP. +.IP "\fBhost \fIhostnameaddr\fP" +True if either the IPv4/v6 source or destination of the packet is \fIhostnameaddr\fP. .IP Any of the above host expressions can be prepended with the keywords, \fBip\fP, \fBarp\fP, \fBrarp\fP, or \fBip6\fP as in: .in +.5i .nf -\fBip host \fIhost\fR +\fBip host \fIhostnameaddr\fR .fi .in -.5i which is equivalent to: .in +.5i .nf -\fBether proto \fI\\ip\fB and host \fIhost\fR +\fBether proto \\\fRip \fBand host \fIhostnameaddr\fR .fi .in -.5i -If \fIhost\fR is a name with multiple IP addresses, each address will +If \fIhostnameaddr\fR is a name with multiple IPv4/v6 addresses, each address will be checked for a match. -.IP "\fBether dst \fIehost\fP" -True if the Ethernet destination address is \fIehost\fP. -\fIEhost\fP +.IP "\fBether dst \fIethernameaddr\fP" +True if the Ethernet destination address is \fIethernameaddr\fP. +\fIethernameaddr\fP may be either a name from /etc/ethers or a numerical MAC address of the form "xx:xx:xx:xx:xx:xx", "xx.xx.xx.xx.xx.xx", "xx-xx-xx-xx-xx-xx", "xxxx.xxxx.xxxx", "xxxxxxxxxxxx", or various mixes of ':', '.', and '-', where each "x" is a hex digit (0-9, a-f, or A-F). -.IP "\fBether src \fIehost\fP" -True if the Ethernet source address is \fIehost\fP. -.IP "\fBether host \fIehost\fP" -True if either the Ethernet source or destination address is \fIehost\fP. +.IP "\fBether src \fIethernameaddr\fP" +True if the Ethernet source address is \fIethernameaddr\fP. +.IP "\fBether host \fIethernameaddr\fP" +True if either the Ethernet source or destination address is \fIethernameaddr\fP. .IP "\fBgateway\fP \fIhost\fP" True if the packet used \fIhost\fP as a gateway. I.e., the Ethernet @@ -195,14 +196,14 @@ host-name-to-Ethernet-address resolution mechanism (/etc/ethers, etc.). (An equivalent expression is .in +.5i .nf -\fBether host \fIehost \fBand not host \fIhost\fR +\fBether host \fIethernameaddr \fBand not host \fIhostnameaddr\fR .fi .in -.5i -which can be used with either names or numbers for \fIhost / ehost\fP.) +which can be used with either names or numbers for \fIhostnameaddr / ethernameaddr\fP.) This syntax does not work in IPv6-enabled configuration at this moment. -.IP "\fBdst net \fInet\fR" +.IP "\fBdst net \fInetnameaddr\fR" True if the IPv4/v6 destination address of the packet has a network -number of \fInet\fP. +number of \fInetnameaddr\fP. \fINet\fP may be either a name from the networks database (/etc/networks, etc.) or a network number. An IPv4 network number can be written as a dotted quad (e.g., 192.168.1.0), @@ -213,68 +214,68 @@ triple, 255.255.0.0 for a dotted pair, or 255.0.0.0 for a single number. An IPv6 network number must be written out fully; the netmask is ff:ff:ff:ff:ff:ff:ff:ff, so IPv6 "network" matches are really always host matches, and a network match requires a netmask length. -.IP "\fBsrc net \fInet\fR" +.IP "\fBsrc net \fInetnameaddr\fR" True if the IPv4/v6 source address of the packet has a network -number of \fInet\fP. -.IP "\fBnet \fInet\fR" +number of \fInetnameaddr\fP. +.IP "\fBnet \fInetnameaddr\fR" True if either the IPv4/v6 source or destination address of the packet has a network -number of \fInet\fP. -.IP "\fBnet \fInet\fR \fBmask \fInetmask\fR" -True if the IPv4 address matches \fInet\fR with the specific \fInetmask\fR. +number of \fInetnameaddr\fP. +.IP "\fBnet \fInetaddr\fR \fBmask \fInetmask\fR" +True if the IPv4 address matches \fInetaddr\fR with the specific \fInetmask\fR. May be qualified with \fBsrc\fR or \fBdst\fR. -Note that this syntax is not valid for IPv6 \fInet\fR. -.IP "\fBnet \fInet\fR/\fIlen\fR" -True if the IPv4/v6 address matches \fInet\fR with a netmask \fIlen\fR +Note that this syntax is not valid for IPv6 \fInetaddr\fR. +.IP "\fBnet \fInetaddr\fR/\fIlen\fR" +True if the IPv4/v6 address matches \fInetaddr\fR with a netmask \fIlen\fR bits wide. May be qualified with \fBsrc\fR or \fBdst\fR. -.IP "\fBdst port \fIport\fR" -True if the packet is ip/tcp, ip/udp, ip6/tcp or ip6/udp and has a -destination port value of \fIport\fP. -The \fIport\fP can be a number or a name used in /etc/services (see -.IR tcp (4P) +.IP "\fBdst port \fIportnamenum\fR" +True if the packet is IPv4/v6 TCP, UDP or SCTP and has a +destination port value of \fIportnamenum\fP. +The \fIportnamenum\fP can be a number or a name used in /etc/services (see +.BR tcp (4P) and -.IR udp (4P)). +.BR udp (4P)). If a name is used, both the port number and protocol are checked. If a number or ambiguous name is used, -only the port number is checked (e.g., \fBdst port 513\fR will print both -tcp/login traffic and udp/who traffic, and \fBport domain\fR will print +only the port number is checked (e.g., `\fBdst port\fR 513' will print both +tcp/login traffic and udp/who traffic, and `\fBport\fR domain' will print both tcp/domain and udp/domain traffic). -.IP "\fBsrc port \fIport\fR" -True if the packet has a source port value of \fIport\fP. -.IP "\fBport \fIport\fR" -True if either the source or destination port of the packet is \fIport\fP. -.IP "\fBdst portrange \fIport1\fB-\fIport2\fR" -True if the packet is ip/tcp, ip/udp, ip6/tcp or ip6/udp and has a -destination port value between \fIport1\fP and \fIport2\fP. -.I port1 +.IP "\fBsrc port \fIportnamenum\fR" +True if the packet has a source port value of \fIportnamenum\fP. +.IP "\fBport \fIportnamenum\fR" +True if either the source or destination port of the packet is \fIportnamenum\fP. +.IP "\fBdst portrange \fIportnamenum1-portnamenum2\fR" +True if the packet is IPv4/v6 TCP, UDP or SCTP and has a +destination port value between \fIportnamenum1\fP and \fIportnamenum2\fP (both inclusive). +.I portnamenum1 and -.I port2 +.I portnamenum2 are interpreted in the same fashion as the -.I port +.I portnamenum parameter for .BR port . -.IP "\fBsrc portrange \fIport1\fB-\fIport2\fR" -True if the packet has a source port value between \fIport1\fP and -\fIport2\fP. -.IP "\fBportrange \fIport1\fB-\fIport2\fR" +.IP "\fBsrc portrange \fIportnamenum1-portnamenum2\fR" +True if the packet has a source port value between \fIportnamenum1\fP and +\fIportnamenum2\fP (both inclusive). +.IP "\fBportrange \fIportnamenum1-portnamenum2\fR" True if either the source or destination port of the packet is between -\fIport1\fP and \fIport2\fP. +\fIportnamenum1\fP and \fIportnamenum2\fP (both inclusive). .IP Any of the above port or port range expressions can be prepended with -the keywords, \fBtcp\fP or \fBudp\fP, as in: +the keywords, \fBtcp\fP, \fBudp\fP or \fBsctp\fP, as in: .in +.5i .nf -\fBtcp src port \fIport\fR +\fBtcp src port \fIportnamenum\fR .fi .in -.5i -which matches only tcp packets whose source port is \fIport\fP. +which matches only TCP packets whose source port is \fIportnamenum\fP. .IP "\fBless \fIlength\fR" True if the packet has a length less than or equal to \fIlength\fP. This is equivalent to: .in +.5i .nf -\fBlen <= \fIlength\fP. +\fBlen <= \fIlength\fP .fi .in -.5i .IP "\fBgreater \fIlength\fR" @@ -282,42 +283,78 @@ True if the packet has a length greater than or equal to \fIlength\fP. This is equivalent to: .in +.5i .nf -\fBlen >= \fIlength\fP. +\fBlen >= \fIlength\fP .fi .in -.5i .IP "\fBip proto \fIprotocol\fR" True if the packet is an IPv4 packet (see -.IR ip (4P)) +.BR ip (4P)) of protocol type \fIprotocol\fP. -\fIProtocol\fP can be a number or one of the names -\fBicmp\fP, \fBicmp6\fP, \fBigmp\fP, \fBigrp\fP, \fBpim\fP, \fBah\fP, -\fBesp\fP, \fBvrrp\fP, \fBudp\fP, or \fBtcp\fP. -Note that the identifiers \fBtcp\fP, \fBudp\fP, and \fBicmp\fP are also -keywords and must be escaped via backslash (\\). +\fIProtocol\fP can be a number or one of the names recognized by +.BR getprotobyname (3) +(as in e.g. `\fBgetent\fR(1) protocols'), typically from an entry in +.IR \%/etc/protocols , +for example: +.BR ah , +.BR esp , +.B eigrp +(only in Linux, FreeBSD, NetBSD, DragonFly BSD, and macOS), +.BR icmp , +.BR igmp , +.B igrp +(only in OpenBSD), +.BR pim , +.BR sctp , +.BR tcp , +.B udp +or +.BR vrrp . +Note that most of these example identifiers +are also keywords and must be escaped via backslash (\\). Note that this primitive does not chase the protocol header chain. +.IP "\fBicmp\fR" +Abbreviation for: +.in +.5i +.nf +\fBip proto\fR 1 +.fi +.in -.5i .IP "\fBip6 proto \fIprotocol\fR" True if the packet is an IPv6 packet of protocol type \fIprotocol\fP. +(See `\fBip proto\fP' above for the meaning of \fIprotocol\fR.) +Note that the IPv6 variant of ICMP uses a different protocol number, named +.B \%ipv6-icmp +in AIX, FreeBSD, illumos, Linux, macOS, NetBSD, OpenBSD, Solaris and Windows. Note that this primitive does not chase the protocol header chain. +.IP "\fBicmp6\fR" +Abbreviation for: +.in +.5i +.nf +\fBip6 proto\fR 58 +.fi +.in -.5i .IP "\fBproto \fIprotocol\fR" True if the packet is an IPv4 or IPv6 packet of protocol type -\fIprotocol\fP. Note that this primitive does not chase the protocol +\fIprotocol\fP. (See `\fBip proto\fP' above for the meaning of +\fIprotocol\fP.) Note that this primitive does not chase the protocol header chain. -.IP "\fBtcp\fR, \fBudp\fR, \fBicmp\fR" +.IP "\fBah\fR, \fBesp\fR, \fBpim\fR, \fBsctp\fR, \fBtcp\fR, \fBudp\fR" Abbreviations for: .in +.5i .nf -\fBproto \fIp\fR\fB +\fBproto \\\fIprotocol\fR .fi .in -.5i -where \fIp\fR is one of the above protocols. +where \fIprotocol\fR is one of the above protocols. .IP "\fBip6 protochain \fIprotocol\fR" True if the packet is IPv6 packet, and contains protocol header with type \fIprotocol\fR in its protocol header chain. +(See `\fBip proto\fP' above for the meaning of \fIprotocol\fP.) For example, .in +.5i .nf -\fBip6 protochain 6\fR +\fBip6 protochain\fR 6 .fi .in -.5i matches any IPv6 packet with TCP protocol header in the protocol header chain. @@ -330,13 +367,15 @@ filter engines in the kernel, so this can be somewhat slow, and may cause more packets to be dropped. .IP "\fBip protochain \fIprotocol\fR" Equivalent to \fBip6 protochain \fIprotocol\fR, but this is for IPv4. +(See `\fBip proto\fP' above for the meaning of \fIprotocol\fP.) .IP "\fBprotochain \fIprotocol\fR" True if the packet is an IPv4 or IPv6 packet of protocol type -\fIprotocol\fP. Note that this primitive chases the protocol +\fIprotocol\fP. (See `\fBip proto\fP' above for the meaning of +\fIprotocol\fP.) Note that this primitive chases the protocol header chain. .IP "\fBether broadcast\fR" True if the packet is an Ethernet broadcast packet. -The \fIether\fP +The \fBether\fP keyword is optional. .IP "\fBip broadcast\fR" True if the packet is an IPv4 broadcast packet. @@ -353,7 +392,7 @@ check will not work correctly. True if the packet is an Ethernet multicast packet. The \fBether\fP keyword is optional. -This is shorthand for `\fBether[0] & 1 != 0\fP'. +This is shorthand for `\fBether[\fP0\fB] & \fP1\fB != \fP0'. .IP "\fBip multicast\fR" True if the packet is an IPv4 multicast packet. .IP "\fBip6 multicast\fR" @@ -361,15 +400,15 @@ True if the packet is an IPv6 multicast packet. .IP "\fBether proto \fIprotocol\fR" True if the packet is of ether type \fIprotocol\fR. \fIProtocol\fP can be a number or one of the names -\fBip\fP, \fBip6\fP, \fBarp\fP, \fBrarp\fP, \fBatalk\fP, \fBaarp\fP, -\fBdecnet\fP, \fBsca\fP, \fBlat\fP, \fBmopdl\fP, \fBmoprc\fP, -\fBiso\fP, \fBstp\fP, \fBipx\fP, or \fBnetbeui\fP. -Note these identifiers are also keywords +\fBaarp\fP, \fBarp\fP, \fBatalk\fP, \fBdecnet\fP, \fBip\fP, \fBip6\fP, +\fBipx\fP, \fBiso\fP, \fBlat\fP, \fBloopback\fP, \fBmopdl\fP, \fBmoprc\fP, \fBnetbeui\fP, +\fBrarp\fP, \fBsca\fP or \fBstp\fP. +Note these identifiers (except \fBloopback\fP) are also keywords and must be escaped via backslash (\\). .IP -[In the case of FDDI (e.g., `\fBfddi proto arp\fR'), Token Ring -(e.g., `\fBtr proto arp\fR'), and IEEE 802.11 wireless LANS (e.g., -`\fBwlan proto arp\fR'), for most of those protocols, the +[In the case of FDDI (e.g., `\fBfddi proto \\arp\fR'), Token Ring +(e.g., `\fBtr proto \\arp\fR'), and IEEE 802.11 wireless LANs (e.g., +`\fBwlan proto \\arp\fR'), for most of those protocols, the protocol identification comes from the 802.2 Logical Link Control (LLC) header, which is usually layered on top of the FDDI, Token Ring, or 802.11 header. @@ -419,34 +458,34 @@ IPX, and the IPX etype in a SNAP frame. Abbreviations for: .in +.5i .nf -\fBether proto \fIp\fR +\fBether proto \\\fIprotocol\fR .fi .in -.5i -where \fIp\fR is one of the above protocols. +where \fIprotocol\fR is one of the above protocols. .IP "\fBlat\fR, \fBmoprc\fR, \fBmopdl\fR" Abbreviations for: .in +.5i .nf -\fBether proto \fIp\fR +\fBether proto \\\fIprotocol\fR .fi .in -.5i -where \fIp\fR is one of the above protocols. +where \fIprotocol\fR is one of the above protocols. Note that not all applications using .BR pcap (3PCAP) currently know how to parse these protocols. -.IP "\fBdecnet src \fIhost\fR" -True if the DECNET source address is -.IR host , -which may be an address of the form ``10.123'', or a DECNET host +.IP "\fBdecnet src \fIdecnetaddr\fR" +True if the DECnet source address is +.IR decnetaddr , +which may be an address of the form ``10.123'', or a DECnet host name. -[DECNET host name support is only available on ULTRIX systems -that are configured to run DECNET.] -.IP "\fBdecnet dst \fIhost\fR" -True if the DECNET destination address is -.IR host . -.IP "\fBdecnet host \fIhost\fR" -True if either the DECNET source or destination address is -.IR host . +[DECnet host name support is only available on ULTRIX systems +that are configured to run DECnet.] +.IP "\fBdecnet dst \fIdecnetaddr\fR" +True if the DECnet destination address is +.IR decnetaddr . +.IP "\fBdecnet host \fIdecnetaddr\fR" +True if either the DECnet source or destination address is +.IR decnetaddr . .IP \fBllc\fP True if the packet has an 802.2 LLC header. This includes: .IP @@ -460,7 +499,7 @@ Token Ring packets (no check is done for LLC frames); FDDI packets (no check is done for LLC frames); .IP LLC-encapsulated ATM packets, for SunATM on Solaris. -.IP "\fBllc\fP \Fitype\fR" +.IP "\fBllc\fP \fItype\fR" True if the packet has an 802.2 LLC header and has the specified .IR type . .I type @@ -535,11 +574,11 @@ modifier. .IP "\fBreason \fIcode\fR" True if the packet was logged with the specified PF reason code. The known codes are: -.BR match , -.BR bad-offset , -.BR fragment , -.BR short , -.BR normalize , +.BR \%match , +.BR \%bad-offset , +.BR \%fragment , +.BR \%short , +.BR \%normalize , and .B memory (applies only to packets logged by OpenBSD's or FreeBSD's @@ -639,27 +678,28 @@ then valid \fIwlan_subtype\fRs are: .IP If the specified \fIwlan_type\fR is \fBdata\fP, then valid \fIwlan_subtype\fRs are: -\fBdata\fP, -\fBdata-cf-ack\fP, -\fBdata-cf-poll\fP, -\fBdata-cf-ack-poll\fP, -\fBnull\fP, -\fBcf-ack\fP, -\fBcf-poll\fP, -\fBcf-ack-poll\fP, -\fBqos-data\fP, -\fBqos-data-cf-ack\fP, -\fBqos-data-cf-poll\fP, -\fBqos-data-cf-ack-poll\fP, -\fBqos\fP, -\fBqos-cf-poll\fP and -\fBqos-cf-ack-poll\fP. +.BR \%data , +.BR \%data-cf-ack , +.BR \%data-cf-poll , +.BR \%data-cf-ack-poll , +.BR \%null , +.BR \%cf-ack , +.BR \%cf-poll , +.BR \%cf-ack-poll , +.BR \%qos-data , +.BR \%qos-data-cf-ack , +.BR \%qos-data-cf-poll , +.BR \%qos-data-cf-ack-poll , +.BR \%qos , +.B \%qos-cf-poll +and +.BR \%qos-cf-ack-poll . .IP "\fBsubtype \fIwlan_subtype\fR" True if the IEEE 802.11 frame subtype matches the specified \fIwlan_subtype\fR and frame has the type to which the specified \fIwlan_subtype\fR belongs. -.IP "\fBdir \fIdir\fR" +.IP "\fBdir \fIdirection\fR" True if the IEEE 802.11 frame direction matches the specified -.IR dir . +.IR direction . Valid directions are: .BR nods , .BR tods , @@ -668,51 +708,51 @@ Valid directions are: or a numeric value. .IP "\fBvlan \fI[vlan_id]\fR" True if the packet is an IEEE 802.1Q VLAN packet. -If \fI[vlan_id]\fR is specified, only true if the packet has the specified +If the optional \fIvlan_id\fR is specified, only true if the packet has the specified \fIvlan_id\fR. -Note that the first \fBvlan\fR keyword encountered in \fIexpression\fR -changes the decoding offsets for the remainder of \fIexpression\fR on -the assumption that the packet is a VLAN packet. The \fBvlan -\fI[vlan_id]\fR expression may be used more than once, to filter on VLAN -hierarchies. Each use of that expression increments the filter offsets +Note that the first \fBvlan\fR keyword encountered in an expression +changes the decoding offsets for the remainder of the expression on +the assumption that the packet is a VLAN packet. The `\fBvlan +\fI[vlan_id]\fR` keyword may be used more than once, to filter on VLAN +hierarchies. Each use of that keyword increments the filter offsets by 4. .IP For example: .in +.5i .nf -\fBvlan 100 && vlan 200\fR +\fBvlan\fP 100 \fB&& vlan\fR 200 .fi .in -.5i filters on VLAN 200 encapsulated within VLAN 100, and .in +.5i .nf -\fBvlan && vlan 300 && ip\fR +\fBvlan && vlan \fP300 \fB&& ip\fR .fi .in -.5i -filters IPv4 protocols encapsulated in VLAN 300 encapsulated within any +filters IPv4 protocol encapsulated in VLAN 300 encapsulated within any higher order VLAN. .IP "\fBmpls \fI[label_num]\fR" True if the packet is an MPLS packet. -If \fI[label_num]\fR is specified, only true is the packet has the specified +If the optional \fIlabel_num\fR is specified, only true if the packet has the specified \fIlabel_num\fR. -Note that the first \fBmpls\fR keyword encountered in \fIexpression\fR -changes the decoding offsets for the remainder of \fIexpression\fR on +Note that the first \fBmpls\fR keyword encountered in an expression +changes the decoding offsets for the remainder of the expression on the assumption that the packet is a MPLS-encapsulated IP packet. The -\fBmpls \fI[label_num]\fR expression may be used more than once, to -filter on MPLS hierarchies. Each use of that expression increments the +`\fBmpls \fI[label_num]\fR` keyword may be used more than once, to +filter on MPLS hierarchies. Each use of that keyword increments the filter offsets by 4. .IP For example: .in +.5i .nf -\fBmpls 100000 && mpls 1024\fR +\fBmpls\fP 100000 \fB&& mpls\fR 1024 .fi .in -.5i filters packets with an outer label of 100000 and an inner label of 1024, and .in +.5i .nf -\fBmpls && mpls 1024 && host 192.9.200.1\fR +\fBmpls && mpls\fP 1024 \fB&& host\fR 192.9.200.1 .fi .in -.5i filters packets to or from 192.9.200.1 with an inner label of 1024 and @@ -723,34 +763,34 @@ type 0x8863). .IP "\fBpppoes \fI[session_id]\fR" True if the packet is a PPP-over-Ethernet Session packet (Ethernet type 0x8864). -If \fI[session_id]\fR is specified, only true if the packet has the specified +If the optional \fIsession_id\fR is specified, only true if the packet has the specified \fIsession_id\fR. -Note that the first \fBpppoes\fR keyword encountered in \fIexpression\fR -changes the decoding offsets for the remainder of \fIexpression\fR on +Note that the first \fBpppoes\fR keyword encountered in an expression +changes the decoding offsets for the remainder of the expression on the assumption that the packet is a PPPoE session packet. .IP For example: .in +.5i .nf -\fBpppoes 0x27 && ip\fR +\fBpppoes\fP 0x27 \fB&& ip\fR .fi .in -.5i -filters IPv4 protocols encapsulated in PPPoE session id 0x27. +filters IPv4 protocol encapsulated in PPPoE session id 0x27. .IP "\fBgeneve \fI[vni]\fR" -True if the packet is a Geneve packet (UDP port 6081). If \fI[vni]\fR +True if the packet is a Geneve packet (UDP port 6081). If the optional \fIvni\fR is specified, only true if the packet has the specified \fIvni\fR. Note that when the \fBgeneve\fR keyword is encountered in -\fIexpression\fR, it changes the decoding offsets for the remainder of -\fIexpression\fR on the assumption that the packet is a Geneve packet. +an expression, it changes the decoding offsets for the remainder of +the expression on the assumption that the packet is a Geneve packet. .IP For example: .in +.5i .nf -\fBgeneve 0xb && ip\fR +\fBgeneve\fP 0xb \fB&& ip\fR .fi .in -.5i -filters IPv4 protocols encapsulated in Geneve with VNI 0xb. This will -match both IP directly encapsulated in Geneve as well as IP contained +filters IPv4 protocol encapsulated in Geneve with VNI 0xb. This will +match both IPv4 directly encapsulated in Geneve as well as IPv4 contained inside an Ethernet frame. .IP "\fBiso proto \fIprotocol\fR" True if the packet is an OSI packet of protocol type \fIprotocol\fP. @@ -760,10 +800,10 @@ True if the packet is an OSI packet of protocol type \fIprotocol\fP. Abbreviations for: .in +.5i .nf -\fBiso proto \fIp\fR +\fBiso proto \\\fIprotocol\fR .fi .in -.5i -where \fIp\fR is one of the above protocols. +where \fIprotocol\fR is one of the above protocols. .IP "\fBl1\fR, \fBl2\fR, \fBiih\fR, \fBlsp\fR, \fBsnp\fR, \fBcsnp\fR, \fBpsnp\fR" Abbreviations for IS-IS PDU types. .IP "\fBvpi\fP \fIn\fR" @@ -777,8 +817,8 @@ virtual channel identifier of .IP \fBlane\fP True if the packet is an ATM packet, for SunATM on Solaris, and is an ATM LANE packet. -Note that the first \fBlane\fR keyword encountered in \fIexpression\fR -changes the tests done in the remainder of \fIexpression\fR +Note that the first \fBlane\fR keyword encountered in an expression +changes the tests done in the remainder of the expression on the assumption that the packet is either a LANE emulated Ethernet packet or a LANE LE Control packet. If \fBlane\fR isn't specified, the tests are done under the assumption that the packet is an @@ -815,82 +855,175 @@ Connect Ack, Release, or Release Done message. True if the packet is an ATM packet, for SunATM on Solaris, and is on a meta signaling circuit and is a Q.2931 Setup, Call Proceeding, Connect, Release, or Release Done message. -.IP "\fIexpr relop expr\fR" -True if the relation holds, where \fIrelop\fR is one of >, <, >=, <=, =, -!=, and \fIexpr\fR is an arithmetic expression composed of integer -constants (expressed in standard C syntax), the normal binary operators -[+, -, *, /, %, &, |, ^, <<, >>], a length operator, and special packet data +.IP "\fIexpr1 relop expr2\fR" +True if the relation holds. \fIRelop\fR is one of +.RB { > , +.BR < , +.BR >= , +.BR <= , +.BR = , +.BR == , +.BR != } +(where +.B = +means the same as +.BR == ). +Each of \fIexpr1\fR and \fIexpr2\fR is an arithmetic expression composed of +integer constants (expressed in standard C syntax), the normal binary operators +.RB { + , +.BR - , +.BR * , +.BR / , +.BR % , +.BR & , +.BR | , +.BR ^ , +.BR << , +.BR >> }, +a length operator, and special packet data accessors. Note that all comparisons are unsigned, so that, for example, 0x80000000 and 0xffffffff are > 0. .IP -The % and ^ operators are currently only supported for filtering in the -kernel on Linux with 3.7 and later kernels; on all other systems, if +The +.B % +and +.B ^ +operators are currently only supported for filtering in the kernel on +particular operating systems (for example: FreeBSD, Linux with 3.7 and later +kernels, NetBSD); on all other systems (for example: AIX, illumos, Solaris, +OpenBSD), if those operators are used, filtering will be done in user mode, which will increase the overhead of capturing packets and may cause more packets to be dropped. .IP +The length operator, indicated by the keyword \fBlen\fP, gives the +length of the packet. +.IP To access data inside the packet, use the following syntax: .in +.5i .nf \fIproto\fB [ \fIexpr\fB : \fIsize\fB ]\fR .fi .in -.5i -\fIProto\fR is one of \fBether, fddi, tr, wlan, ppp, slip, link, -ip, arp, rarp, tcp, udp, icmp, ip6\fR or \fBradio\fR, and +.I Proto +is one of +.BR arp , +.BR atalk , +.BR carp , +.BR decnet , +.BR ether , +.BR fddi , +.BR icmp , +.BR icmp6 , +.BR igmp , +.BR igrp , +.BR ip , +.BR ip6 , +.BR lat , +.BR link , +.BR mopdl , +.BR moprc , +.BR pim , +.BR ppp , +.BR radio , +.BR rarp , +.BR sca , +.BR sctp , +.BR slip , +.BR tcp , +.BR tr , +.BR udp , +.B vrrp +or +.BR wlan , +and indicates the protocol layer for the index operation. -(\fBether, fddi, wlan, tr, ppp, slip\fR and \fBlink\fR all refer to the +.RB ( ether , +.BR fddi , +.BR link , +.BR ppp , +.BR slip , +.B tr +and +.BR wlan +all refer to the link layer. \fBradio\fR refers to the "radio header" added to some 802.11 captures.) -Note that \fItcp, udp\fR and other upper-layer protocol types only +Note that \fBtcp\fR, \fBudp\fR and other upper-layer protocol types only apply to IPv4, not IPv6 (this will be fixed in the future). The byte offset, relative to the indicated protocol layer, is given by \fIexpr\fR. \fISize\fR is optional and indicates the number of bytes in the field of interest; it can be either one, two, or four, and defaults to one. -The length operator, indicated by the keyword \fBlen\fP, gives the -length of the packet. -For example, `\fBether[0] & 1 != 0\fP' catches all multicast traffic. -The expression `\fBip[0] & 0xf != 5\fP' +For example, `\fBether[\fP0\fB] &\fP 1 \fB!=\fP 0' catches all multicast traffic. +The expression `\fBip[\fP0\fB] &\fP 0xf \fB!=\fP 5' catches all IPv4 packets with options. The expression -`\fBip[6:2] & 0x1fff = 0\fP' +`\fBip[\fP6:2\fB] &\fP 0x1fff \fB=\fP 0' catches only unfragmented IPv4 datagrams and frag zero of fragmented IPv4 datagrams. This check is implicitly applied to the \fBtcp\fP and \fBudp\fP index operations. -For instance, \fBtcp[0]\fP always means the first +For instance, \fBtcp[\fP0\fB]\fP always means the first byte of the TCP \fIheader\fP, and never means the first byte of an intervening fragment. - +.IP Some offsets and field values may be expressed as names rather than as numeric values. The following protocol header field offsets are -available: \fBicmptype\fP (ICMP type field), \fBicmp6type (ICMP v6 type field) -\fBicmpcode\fP (ICMP code field), \fBicmp6code\fP (ICMP v6 code field), and +available: \fBicmptype\fP (ICMP type field), \fBicmp6type\fP (ICMPv6 type field), +\fBicmpcode\fP (ICMP code field), \fBicmp6code\fP (ICMPv6 code field) and \fBtcpflags\fP (TCP flags field). - -The following ICMP type field values are available: \fBicmp-echoreply\fP, -\fBicmp-unreach\fP, \fBicmp-sourcequench\fP, \fBicmp-redirect\fP, -\fBicmp-echo\fP, \fBicmp-routeradvert\fP, \fBicmp-routersolicit\fP, -\fBicmp-timxceed\fP, \fBicmp-paramprob\fP, \fBicmp-tstamp\fP, -\fBicmp-tstampreply\fP, \fBicmp-ireq\fP, \fBicmp-ireqreply\fP, -\fBicmp-maskreq\fP, \fBicmp-maskreply\fP. - -The following ICMPv6 type fields are available: \fBicmp6-echo\fP, -\fBicmp6-echoreply\fP, \fBicmp6-multicastlistenerquery\fP, -\fBicmp6-multicastlistenerreportv1\fP, \fBicmp6-multicastlistenerdone\fP, -\fBicmp6-routersolicit\fP, \fBicmp6-routeradvert\fP, -\fBicmp6-neighborsolicit\fP, \fBicmp6-neighboradvert\fP, \fBicmp6-redirect\fP, -\fBicmp6-routerrenum\fP, \fBicmp6-nodeinformationquery\fP, -\fBicmp6-nodeinformationresponse\fP, \fBicmp6-ineighbordiscoverysolicit\fP, -\fBicmp6-ineighbordiscoveryadvert\fP, \fBicmp6-multicastlistenerreportv2\fP, -\fBicmp6-homeagentdiscoveryrequest\fP, \fBicmp6-homeagentdiscoveryreply\fP, -\fBicmp6-mobileprefixsolicit\fP, \fBicmp6-mobileprefixadvert\fP, -\fBicmp6-certpathsolicit\fP, \fBicmp6-certpathadvert\fP, -\fBicmp6-multicastrouteradvert\fP, \fBicmp6-multicastroutersolicit\fP, -\fBicmp6-multicastrouterterm\fP. - +.IP +The following ICMP type field values are available: +.BR \%icmp-echoreply , +.BR \%icmp-unreach , +.BR \%icmp-sourcequench , +.BR \%icmp-redirect , +.BR \%icmp-echo , +.BR \%icmp-routeradvert , +.BR \%icmp-routersolicit , +.BR \%icmp-timxceed , +.BR \%icmp-paramprob , +.BR \%icmp-tstamp , +.BR \%icmp-tstampreply , +.BR \%icmp-ireq , +.BR \%icmp-ireqreply , +.BR \%icmp-maskreq , +.BR \%icmp-maskreply . +.IP +The following ICMPv6 type field values are available: +.BR \%icmp6-destinationunreach , +.BR \%icmp6-packettoobig , +.BR \%icmp6-timeexceeded , +.BR \%icmp6-parameterproblem , +.BR \%icmp6-echo , +.BR \%icmp6-echoreply , +.BR \%icmp6-multicastlistenerquery , +.BR \%icmp6-multicastlistenerreportv1 , +.BR \%icmp6-multicastlistenerdone , +.BR \%icmp6-routersolicit , +.BR \%icmp6-routeradvert , +.BR \%icmp6-neighborsolicit , +.BR \%icmp6-neighboradvert , +.BR \%icmp6-redirect , +.BR \%icmp6-routerrenum , +.BR \%icmp6-nodeinformationquery , +.BR \%icmp6-nodeinformationresponse , +.BR \%icmp6-ineighbordiscoverysolicit , +.BR \%icmp6-ineighbordiscoveryadvert , +.BR \%icmp6-multicastlistenerreportv2 , +.BR \%icmp6-homeagentdiscoveryrequest , +.BR \%icmp6-homeagentdiscoveryreply , +.BR \%icmp6-mobileprefixsolicit , +.BR \%icmp6-mobileprefixadvert , +.BR \%icmp6-certpathsolicit , +.BR \%icmp6-certpathadvert , +.BR \%icmp6-multicastrouteradvert , +.BR \%icmp6-multicastroutersolicit , +.BR \%icmp6-multicastrouterterm . +.IP The following TCP flags field values are available: \fBtcp-fin\fP, \fBtcp-syn\fP, \fBtcp-rst\fP, \fBtcp-push\fP, \fBtcp-ack\fP, \fBtcp-urg\fP, \fBtcp-ece\fP, @@ -906,7 +1039,7 @@ Concatenation (`\fB&&\fP' or `\fBand\fP'). .IP Alternation (`\fB||\fP' or `\fBor\fP'). .LP -Negation has highest precedence. +Negation has the highest precedence. Alternation and concatenation have equal precedence and associate left to right. Note that explicit \fBand\fR tokens, not juxtaposition, @@ -917,67 +1050,64 @@ is assumed. For example, .in +.5i .nf -\fBnot host vs and ace\fR +\fBnot host\fP vs \fBand\fR ace .fi .in -.5i is short for .in +.5i .nf -\fBnot host vs and host ace\fR +\fBnot host\fP vs \fBand host\fR ace .fi .in -.5i which should not be confused with .in +.5i .nf -\fBnot ( host vs or ace )\fR +\fBnot (host \fPvs\fB or \fPace\fB)\fR .fi .in -.5i .SH EXAMPLES .LP -To select all packets arriving at or departing from \fIsundown\fP: +To select all packets arriving at or departing from `sundown': .RS .nf -\fBhost sundown\fP +\fBhost\fP sundown .fi .RE .LP -To select traffic between \fIhelios\fR and either \fIhot\fR or \fIace\fR: +To select traffic between `helios' and either `hot' or `ace': .RS .nf -\fBhost helios and \\( hot or ace \\)\fP +\fBhost\fP helios \fBand (\fPhot \fBor\fP ace\fB)\fP .fi .RE .LP -To select all IP packets between \fIace\fR and any host except \fIhelios\fR: +To select all IPv4 packets between `ace' and any host except `helios': .RS .nf -\fBip host ace and not helios\fP +\fBip host\fP ace \fBand not\fP helios .fi .RE .LP To select all traffic between local hosts and hosts at Berkeley: .RS .nf -.B -net ucb-ether +\fBnet\fP ucb-ether .fi .RE .LP -To select all ftp traffic through internet gateway \fIsnup\fP: +To select all FTP traffic through Internet gateway `snup': .RS .nf -.B -gateway snup and (port ftp or ftp-data) +\fBgateway\fP snup \fBand (port\fP ftp \fBor\fP ftp-data\fB)\fP .fi .RE .LP -To select traffic neither sourced from nor destined for local hosts +To select IPv4 traffic neither sourced from nor destined for local hosts (if you gateway to one other net, this stuff should never make it onto your local net). .RS .nf -.B -ip and not net \fIlocalnet\fP +\fBip and not net \fPlocalnet .fi .RE .LP @@ -985,8 +1115,17 @@ To select the start and end packets (the SYN and FIN packets) of each TCP conversation that involves a non-local host. .RS .nf +\fBtcp[tcpflags] & (tcp-syn|tcp-fin) !=\fP 0 \fBand not src and dst net\fP localnet +.fi +.RE +.LP +To select the TCP packets with flags RST and ACK both set. +(i.e. select only the RST and ACK flags in the flags field, and if the result +is "RST and ACK both set", match) +.RS +.nf .B -tcp[tcpflags] & (tcp-syn|tcp-fin) != 0 and not src and dst net \fIlocalnet\fP +tcp[tcpflags] & (tcp-rst|tcp-ack) == (tcp-rst|tcp-ack) .fi .RE .LP @@ -995,26 +1134,23 @@ packets that contain data, not, for example, SYN and FIN packets and ACK-only packets. (IPv6 is left as an exercise for the reader.) .RS .nf -.B -tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0) +\fBtcp port\fP 80 \fBand (((ip[\fP2:2\fB] - ((ip[\fP0\fB]&\fP0xf\fB)<<\fP2\fB)) - ((tcp[\fP12\fB]&\fP0xf0\fB)>>\fP2\fB)) != \fP0\fB) .fi .RE .LP -To select IP packets longer than 576 bytes sent through gateway \fIsnup\fP: +To select IPv4 packets longer than 576 bytes sent through gateway `snup': .RS .nf -.B -gateway snup and ip[2:2] > 576 +\fBgateway\fP snup \fBand ip[\fP2:2\fB] >\fP 576 .fi .RE .LP -To select IP broadcast or multicast packets that were +To select IPv4 broadcast or multicast packets that were .I not sent via Ethernet broadcast or multicast: .RS .nf -.B -ether[0] & 1 = 0 and ip[16] >= 224 +\fBether[\fP0\fB] &\fP 1 \fB=\fP 0 \fBand ip[\fP16\fB] >=\fP 224 .fi .RE .LP @@ -1024,16 +1160,28 @@ ping packets): .nf .B icmp[icmptype] != icmp-echo and icmp[icmptype] != icmp-echoreply +.B +icmp6[icmp6type] != icmp6-echo and icmp6[icmp6type] != icmp6-echoreply .fi .RE -.SH "SEE ALSO" -pcap(3PCAP) +.SH BACKWARD COMPATIBILITY +The ICMPv6 type code names, as well as the +.B tcp-ece +and +.B tcp-cwr +TCP flag names became available in libpcap 1.9.0. +.PP +The +.B geneve +keyword became available in libpcap 1.8.0. +.SH SEE ALSO +.BR pcap (3PCAP) .SH BUGS -To report a security issue please send an e-mail to security@tcpdump.org. +To report a security issue please send an e-mail to \%security@tcpdump.org. .LP To report bugs and other problems, contribute patches, request a feature, provide generic feedback etc please see the file -.I CONTRIBUTING +.I CONTRIBUTING.md in the libpcap source tree root. .LP Filter expressions on fields other than those in Token Ring headers will @@ -1042,10 +1190,11 @@ not correctly handle source-routed Token Ring packets. Filter expressions on fields other than those in 802.11 headers will not correctly handle 802.11 data packets with both To DS and From DS set. .LP -.BR "ip6 proto" +`\fBip6 proto\fP' should chase header chain, but at this moment it does not. -.BR "ip6 protochain" -is supplied for this behavior. +`\fBip6 protochain\fP' +is supplied for this behavior. For example, to match IPv6 fragments: +`\fBip6 protochain\fP 44' .LP Arithmetic expression against transport layer headers, like \fBtcp[0]\fP, does not work against IPv6 packets. diff --git a/pcap-haiku.cpp b/pcap-haiku.cpp new file mode 100644 index 000000000000..8ae9119c2ccf --- /dev/null +++ b/pcap-haiku.cpp @@ -0,0 +1,305 @@ +/* + * Copyright 2006-2010, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Axel Dörfler, axeld@pinc-software.de + * James Woodcock + */ + + +#include "config.h" +#include "pcap-int.h" + +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + + +/* + * Private data for capturing on Haiku sockets. + */ +struct pcap_haiku { + struct pcap_stat stat; + char *device; /* device name */ +}; + + +bool +prepare_request(struct ifreq& request, const char* name) +{ + if (strlen(name) >= IF_NAMESIZE) + return false; + + strcpy(request.ifr_name, name); + return true; +} + + +static int +pcap_read_haiku(pcap_t* handle, int maxPackets _U_, pcap_handler callback, + u_char* userdata) +{ + // Receive a single packet + + u_char* buffer = (u_char*)handle->buffer + handle->offset; + struct sockaddr_dl from; + ssize_t bytesReceived; + do { + if (handle->break_loop) { + // Clear the break loop flag, and return -2 to indicate our + // reasoning + handle->break_loop = 0; + return -2; + } + + socklen_t fromLength = sizeof(from); + bytesReceived = recvfrom(handle->fd, buffer, handle->bufsize, MSG_TRUNC, + (struct sockaddr*)&from, &fromLength); + } while (bytesReceived < 0 && errno == B_INTERRUPTED); + + if (bytesReceived < 0) { + if (errno == B_WOULD_BLOCK) { + // there is no packet for us + return 0; + } + + snprintf(handle->errbuf, sizeof(handle->errbuf), + "recvfrom: %s", strerror(errno)); + return -1; + } + + int32 captureLength = bytesReceived; + if (captureLength > handle->snapshot) + captureLength = handle->snapshot; + + // run the packet filter + if (handle->fcode.bf_insns) { + if (pcap_filter(handle->fcode.bf_insns, buffer, bytesReceived, + captureLength) == 0) { + // packet got rejected + return 0; + } + } + + // fill in pcap_header + pcap_pkthdr header; + header.caplen = captureLength; + header.len = bytesReceived; + header.ts.tv_usec = system_time() % 1000000; + header.ts.tv_sec = system_time() / 1000000; + // TODO: get timing from packet!!! + + /* Call the user supplied callback function */ + callback(userdata, &header, buffer); + return 1; +} + + +static int +pcap_inject_haiku(pcap_t *handle, const void *buffer, int size) +{ + // we don't support injecting packets yet + // TODO: use the AF_LINK protocol (we need another socket for this) to + // inject the packets + strlcpy(handle->errbuf, "Sending packets isn't supported yet", + PCAP_ERRBUF_SIZE); + return -1; +} + + +static int +pcap_stats_haiku(pcap_t *handle, struct pcap_stat *stats) +{ + struct pcap_haiku* handlep = (struct pcap_haiku*)handle->priv; + ifreq request; + int socket = ::socket(AF_INET, SOCK_DGRAM, 0); + if (socket < 0) { + return -1; + } + prepare_request(request, handlep->device); + if (ioctl(socket, SIOCGIFSTATS, &request, sizeof(struct ifreq)) < 0) { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "pcap_stats: %s", + strerror(errno)); + close(socket); + return -1; + } + + close(socket); + handlep->stat.ps_recv += request.ifr_stats.receive.packets; + handlep->stat.ps_drop += request.ifr_stats.receive.dropped; + *stats = handlep->stat; + return 0; +} + + +static int +pcap_activate_haiku(pcap_t *handle) +{ + struct pcap_haiku* handlep = (struct pcap_haiku*)handle->priv; + + const char* device = handle->opt.device; + + handle->read_op = pcap_read_haiku; + handle->setfilter_op = install_bpf_program; /* no kernel filtering */ + handle->inject_op = pcap_inject_haiku; + handle->stats_op = pcap_stats_haiku; + + // use default hooks where possible + handle->getnonblock_op = pcap_getnonblock_fd; + handle->setnonblock_op = pcap_setnonblock_fd; + + /* + * Turn a negative snapshot value (invalid), a snapshot value of + * 0 (unspecified), or a value bigger than the normal maximum + * value, into the maximum allowed value. + * + * If some application really *needs* a bigger snapshot + * length, we should just increase MAXIMUM_SNAPLEN. + */ + if (handle->snapshot <= 0 || handle->snapshot > MAXIMUM_SNAPLEN) + handle->snapshot = MAXIMUM_SNAPLEN; + + handlep->device = strdup(device); + if (handlep->device == NULL) { + pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, + errno, "strdup"); + return PCAP_ERROR; + } + + handle->bufsize = 65536; + // TODO: should be determined by interface MTU + + // allocate buffer for monitoring the device + handle->buffer = (u_char*)malloc(handle->bufsize); + if (handle->buffer == NULL) { + pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, + errno, "buffer malloc"); + return PCAP_ERROR; + } + + handle->offset = 0; + handle->linktype = DLT_EN10MB; + // TODO: check interface type! + + return 0; +} + + +// #pragma mark - pcap API + + +extern "C" pcap_t * +pcap_create_interface(const char *device, char *errorBuffer) +{ + // TODO: handle promiscuous mode! + + // we need a socket to talk to the networking stack + int socket = ::socket(AF_INET, SOCK_DGRAM, 0); + if (socket < 0) { + snprintf(errorBuffer, PCAP_ERRBUF_SIZE, + "The networking stack doesn't seem to be available.\n"); + return NULL; + } + + struct ifreq request; + if (!prepare_request(request, device)) { + snprintf(errorBuffer, PCAP_ERRBUF_SIZE, + "Interface name \"%s\" is too long.", device); + close(socket); + return NULL; + } + + // check if the interface exist + if (ioctl(socket, SIOCGIFINDEX, &request, sizeof(request)) < 0) { + snprintf(errorBuffer, PCAP_ERRBUF_SIZE, + "Interface \"%s\" does not exist.\n", device); + close(socket); + return NULL; + } + + close(socket); + // no longer needed after this point + + // get link level interface for this interface + + socket = ::socket(AF_LINK, SOCK_DGRAM, 0); + if (socket < 0) { + snprintf(errorBuffer, PCAP_ERRBUF_SIZE, "No link level: %s\n", + strerror(errno)); + return NULL; + } + + // start monitoring + if (ioctl(socket, SIOCSPACKETCAP, &request, sizeof(struct ifreq)) < 0) { + snprintf(errorBuffer, PCAP_ERRBUF_SIZE, "Cannot start monitoring: %s\n", + strerror(errno)); + close(socket); + return NULL; + } + + struct wrapper_struct { pcap_t __common; struct pcap_haiku __private; }; + pcap_t* handle = pcap_create_common(errorBuffer, + sizeof (struct wrapper_struct), + offsetof (struct wrapper_struct, __private)); + + if (handle == NULL) { + snprintf(errorBuffer, PCAP_ERRBUF_SIZE, "malloc: %s", strerror(errno)); + close(socket); + return NULL; + } + + handle->selectable_fd = socket; + handle->fd = socket; + + handle->activate_op = pcap_activate_haiku; + + return handle; +} + +static int +can_be_bound(const char *name _U_) +{ + return 1; +} + +static int +get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf) +{ + /* TODO */ + if (*flags & PCAP_IF_LOOPBACK) { + /* + * Loopback devices aren't wireless, and "connected"/ + * "disconnected" doesn't apply to them. + */ + *flags |= PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE; + return (0); + } + return (0); +} + +extern "C" int +pcap_platform_finddevs(pcap_if_list_t* _allDevices, char* errorBuffer) +{ + return pcap_findalldevs_interfaces(_allDevices, errorBuffer, can_be_bound, + get_if_flags); +} + +/* + * Libpcap version string. + */ +extern "C" const char * +pcap_lib_version(void) +{ + return (PCAP_VERSION_STRING); +} diff --git a/pcap-int.h b/pcap-int.h index 5295f8fb46cd..894e74afa853 100644 --- a/pcap-int.h +++ b/pcap-int.h @@ -34,13 +34,42 @@ #ifndef pcap_int_h #define pcap_int_h +#include + #include #include +#ifdef MSDOS + #include + #include +#endif + #include "varattrs.h" #include "fmtutils.h" +#include + +#include "portability.h" + +/* + * If we're compiling with Visual Studio, make sure we have at least + * VS 2015 or later, so we have sufficient C99 support. + * + * XXX - verify that we have at least C99 support on UN*Xes? + * + * What about MinGW or various DOS toolchains? We're currently assuming + * sufficient C99 support there. + */ +#if defined(_MSC_VER) + /* + * Compiler is MSVC. Make sure we have VS 2015 or later. + */ + #if _MSC_VER < 1900 + #error "Building libpcap requires VS 2015 or later" + #endif +#endif + /* * Version string. * Uses PACKAGE_VERSION from config.h. @@ -51,10 +80,32 @@ extern "C" { #endif -#ifdef MSDOS - #include - #include -#endif +/* + * If pcap_new_api is set, we disable pcap_lookupdev(), because: + * + * it's not thread-safe, and is marked as deprecated, on all + * platforms; + * + * on Windows, it may return UTF-16LE strings, which the program + * might then pass to pcap_create() (or to pcap_open_live(), which + * then passes them to pcap_create()), requiring pcap_create() to + * check for UTF-16LE strings using a hack, and that hack 1) + * *cannot* be 100% reliable and 2) runs the risk of going past the + * end of the string. + * + * We keep it around in legacy mode for compatibility. + * + * We also disable the aforementioned hack in pcap_create(). + */ +extern int pcap_new_api; + +/* + * If pcap_utf_8_mode is set, on Windows we treat strings as UTF-8. + * + * On UN*Xes, we assume all strings are and should be in UTF-8, regardless + * of the setting of this flag. + */ +extern int pcap_utf_8_mode; /* * Swap byte ordering of unsigned long long timestamp on a big endian @@ -77,7 +128,7 @@ extern "C" { * 1) big enough for maximum-size Linux loopback packets (65549) * and some USB packets captured with USBPcap: * - * http://desowin.org/usbpcap/ + * https://desowin.org/usbpcap/ * * (> 131072, < 262144) * @@ -97,6 +148,18 @@ extern "C" { */ #define MAXIMUM_SNAPLEN 262144 +/* + * Locale-independent macros for testing character types. + * These can be passed any integral value, without worrying about, for + * example, sign-extending char values, unlike the C macros. + */ +#define PCAP_ISDIGIT(c) \ + ((c) >= '0' && (c) <= '9') +#define PCAP_ISXDIGIT(c) \ + (((c) >= '0' && (c) <= '9') || \ + ((c) >= 'A' && (c) <= 'F') || \ + ((c) >= 'a' && (c) <= 'f')) + struct pcap_opt { char *device; int timeout; /* timeout for buffering */ @@ -123,7 +186,7 @@ typedef int (*activate_op_t)(pcap_t *); typedef int (*can_set_rfmon_op_t)(pcap_t *); typedef int (*read_op_t)(pcap_t *, int cnt, pcap_handler, u_char *); typedef int (*next_packet_op_t)(pcap_t *, struct pcap_pkthdr *, u_char **); -typedef int (*inject_op_t)(pcap_t *, const void *, size_t); +typedef int (*inject_op_t)(pcap_t *, const void *, int); typedef void (*save_current_filter_op_t)(pcap_t *, const char *); typedef int (*setfilter_op_t)(pcap_t *, struct bpf_program *); typedef int (*setdirection_op_t)(pcap_t *, pcap_direction_t); @@ -131,6 +194,7 @@ typedef int (*set_datalink_op_t)(pcap_t *, int); typedef int (*getnonblock_op_t)(pcap_t *); typedef int (*setnonblock_op_t)(pcap_t *, int); typedef int (*stats_op_t)(pcap_t *, struct pcap_stat *); +typedef void (*breakloop_op_t)(pcap_t *); #ifdef _WIN32 typedef struct pcap_stat *(*stats_ex_op_t)(pcap_t *, int *); typedef int (*setbuff_op_t)(pcap_t *, int); @@ -200,8 +264,7 @@ struct pcap { int snapshot; int linktype; /* Network linktype */ - int linktype_ext; /* Extended information stored in the linktype field of a file */ - int tzoff; /* timezone offset */ + int linktype_ext; /* Extended information stored in the linktype field of a file */ int offset; /* offset for proper alignment */ int activated; /* true if the capture is really started */ int oldstyle; /* if we're opening with pcap_open_live() */ @@ -239,7 +302,7 @@ struct pcap { * pcap_t's with a required timeout, and the code must be * prepared not to see any packets from the attempt. */ - struct timeval *required_select_timeout; + const struct timeval *required_select_timeout; #endif /* @@ -248,6 +311,9 @@ struct pcap { struct bpf_program fcode; char errbuf[PCAP_ERRBUF_SIZE + 1]; +#ifdef _WIN32 + char acp_errbuf[PCAP_ERRBUF_SIZE + 1]; /* buffer for local code page error strings */ +#endif int dlt_count; u_int *dlt_list; int tstamp_type_count; @@ -270,6 +336,7 @@ struct pcap { getnonblock_op_t getnonblock_op; setnonblock_op_t setnonblock_op; stats_op_t stats_op; + breakloop_op_t breakloop_op; /* * Routine to use as callback for pcap_next()/pcap_next_ex(). @@ -340,7 +407,7 @@ struct pcap_timeval { * * Then supply the changes by forking the branch at * - * https://github.com/the-tcpdump-group/libpcap/issues + * https://github.com/the-tcpdump-group/libpcap/tree/master * * and issuing a pull request, so that future versions of libpcap and * programs that use it (such as tcpdump) will be able to read your new @@ -350,7 +417,7 @@ struct pcap_timeval { struct pcap_sf_pkthdr { struct pcap_timeval ts; /* time stamp */ bpf_u_int32 caplen; /* length of portion present */ - bpf_u_int32 len; /* length this packet (off wire) */ + bpf_u_int32 len; /* length of this packet (off wire) */ }; /* @@ -366,7 +433,7 @@ struct pcap_sf_pkthdr { struct pcap_sf_patched_pkthdr { struct pcap_timeval ts; /* time stamp */ bpf_u_int32 caplen; /* length of portion present */ - bpf_u_int32 len; /* length this packet (off wire) */ + bpf_u_int32 len; /* length of this packet (off wire) */ int index; unsigned short protocol; unsigned char pkt_type; @@ -388,10 +455,6 @@ struct oneshot_userdata { int pcap_offline_read(pcap_t *, int, pcap_handler, u_char *); -#include - -#include "portability.h" - /* * Does the packet count argument to a module's read routine say * "supply packets until you run out of packets"? @@ -418,12 +481,25 @@ int pcap_setnonblock_fd(pcap_t *p, int); * by pcap_create routines. */ pcap_t *pcap_create_interface(const char *, char *); -pcap_t *pcap_create_common(char *, size_t); + +/* + * This wrapper takes an error buffer pointer and a type to use for the + * private data, and calls pcap_create_common(), passing it the error + * buffer pointer, the size for the private data type, in bytes, and the + * offset of the private data from the beginning of the structure, in + * bytes. + */ +#define PCAP_CREATE_COMMON(ebuf, type) \ + pcap_create_common(ebuf, \ + sizeof (struct { pcap_t __common; type __private; }), \ + offsetof (struct { pcap_t __common; type __private; }, __private)) +pcap_t *pcap_create_common(char *, size_t, size_t); int pcap_do_addexit(pcap_t *); void pcap_add_to_pcaps_to_close(pcap_t *); void pcap_remove_from_pcaps_to_close(pcap_t *); void pcap_cleanup_live_common(pcap_t *); int pcap_check_activated(pcap_t *); +void pcap_breakloop_common(pcap_t *); /* * Internal interfaces for "pcap_findalldevs()". @@ -472,7 +548,8 @@ int add_addr_to_if(pcap_if_list_t *, const char *, bpf_u_int32, #endif /* - * Internal interfaces for "pcap_open_offline()". + * Internal interfaces for "pcap_open_offline()" and other savefile + * I/O routines. * * "pcap_open_offline_common()" allocates and fills in a pcap_t, for use * by pcap_open_offline routines. @@ -483,10 +560,46 @@ int add_addr_to_if(pcap_if_list_t *, const char *, bpf_u_int32, * "sf_cleanup()" closes the file handle associated with a pcap_t, if * appropriate, and frees all data common to all modules for handling * savefile types. + * + * "charset_fopen()", in UTF-8 mode on Windows, does an fopen() that + * treats the pathname as being in UTF-8, rather than the local + * code page, on Windows. */ -pcap_t *pcap_open_offline_common(char *ebuf, size_t size); + +/* + * This wrapper takes an error buffer pointer and a type to use for the + * private data, and calls pcap_create_common(), passing it the error + * buffer pointer, the size for the private data type, in bytes, and the + * offset of the private data from the beginning of the structure, in + * bytes. + */ +#define PCAP_OPEN_OFFLINE_COMMON(ebuf, type) \ + pcap_open_offline_common(ebuf, \ + sizeof (struct { pcap_t __common; type __private; }), \ + offsetof (struct { pcap_t __common; type __private; }, __private)) +pcap_t *pcap_open_offline_common(char *ebuf, size_t total_size, + size_t private_data); bpf_u_int32 pcap_adjust_snapshot(bpf_u_int32 linktype, bpf_u_int32 snaplen); void sf_cleanup(pcap_t *p); +#ifdef _WIN32 +FILE *charset_fopen(const char *path, const char *mode); +#else +/* + * On other OSes, just use Boring Old fopen(). + */ +#define charset_fopen(path, mode) fopen((path), (mode)) +#endif + +/* + * Internal interfaces for loading code at run time. + */ +#ifdef _WIN32 +#define pcap_code_handle_t HMODULE +#define pcap_funcptr_t FARPROC + +pcap_code_handle_t pcap_load_code(const char *); +pcap_funcptr_t pcap_find_function(pcap_code_handle_t, const char *); +#endif /* * Internal interfaces for doing user-mode filtering of packets and @@ -497,7 +610,7 @@ void sf_cleanup(pcap_t *p); * Linux kernel when the kernel rejects the filter (requiring us to * run it in userland). It contains VLAN tag information. */ -struct bpf_aux_data { +struct pcap_bpf_aux_data { u_short vlan_tag_present; u_short vlan_tag; }; @@ -506,8 +619,18 @@ struct bpf_aux_data { * Filtering routine that takes the auxiliary data as an additional * argument. */ -u_int bpf_filter_with_aux_data(const struct bpf_insn *, - const u_char *, u_int, u_int, const struct bpf_aux_data *); +u_int pcap_filter_with_aux_data(const struct bpf_insn *, + const u_char *, u_int, u_int, const struct pcap_bpf_aux_data *); + +/* + * Filtering routine that doesn't. + */ +u_int pcap_filter(const struct bpf_insn *, const u_char *, u_int, u_int); + +/* + * Routine to validate a BPF program. + */ +int pcap_validate_filter(const struct bpf_insn *, int); /* * Internal interfaces for both "pcap_create()" and routines that @@ -522,6 +645,16 @@ int install_bpf_program(pcap_t *, struct bpf_program *); int pcap_strcasecmp(const char *, const char *); +/* + * Internal interfaces for pcap_createsrcstr and pcap_parsesrcstr with + * the additional bit of information regarding SSL support (rpcap:// vs. + * rpcaps://). + */ +int pcap_createsrcstr_ex(char *, int, const char *, const char *, + const char *, unsigned char, char *); +int pcap_parsesrcstr_ex(const char *, int *, char *, char *, + char *, unsigned char *, char *); + #ifdef YYDEBUG extern int pcap_debug; #endif diff --git a/pcap-libdlpi.c b/pcap-libdlpi.c index a38da8b6d0ee..70cb5d45f77f 100644 --- a/pcap-libdlpi.c +++ b/pcap-libdlpi.c @@ -46,7 +46,7 @@ /* Forwards. */ static int dlpromiscon(pcap_t *, bpf_u_int32); static int pcap_read_libdlpi(pcap_t *, int, pcap_handler, u_char *); -static int pcap_inject_libdlpi(pcap_t *, const void *, size_t); +static int pcap_inject_libdlpi(pcap_t *, const void *, int); static void pcap_libdlpi_err(const char *, const char *, int, char *); static void pcap_cleanup_libdlpi(pcap_t *); @@ -108,15 +108,24 @@ pcap_activate_libdlpi(pcap_t *p) */ retv = dlpi_open(p->opt.device, &dh, DLPI_RAW|DLPI_PASSIVE); if (retv != DLPI_SUCCESS) { - if (retv == DLPI_ELINKNAMEINVAL || retv == DLPI_ENOLINK) + if (retv == DLPI_ELINKNAMEINVAL || retv == DLPI_ENOLINK) { + /* + * There's nothing more to say, so clear the + * error message. + */ status = PCAP_ERROR_NO_SUCH_DEVICE; - else if (retv == DL_SYSERR && - (errno == EPERM || errno == EACCES)) + p->errbuf[0] = '\0'; + } else if (retv == DL_SYSERR && + (errno == EPERM || errno == EACCES)) { status = PCAP_ERROR_PERM_DENIED; - else + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Attempt to open DLPI device failed with %s - root privilege may be required", + (errno == EPERM) ? "EPERM" : "EACCES"); + } else { status = PCAP_ERROR; - pcap_libdlpi_err(p->opt.device, "dlpi_open", retv, - p->errbuf); + pcap_libdlpi_err(p->opt.device, "dlpi_open", retv, + p->errbuf); + } return (status); } pd->dlpi_hd = dh; @@ -265,12 +274,25 @@ dlpromiscon(pcap_t *p, bpf_u_int32 level) retv = dlpi_promiscon(pd->dlpi_hd, level); if (retv != DLPI_SUCCESS) { if (retv == DL_SYSERR && - (errno == EPERM || errno == EACCES)) - err = PCAP_ERROR_PERM_DENIED; - else + (errno == EPERM || errno == EACCES)) { + if (level == DL_PROMISC_PHYS) { + err = PCAP_ERROR_PROMISC_PERM_DENIED; + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Attempt to set promiscuous mode failed with %s - root privilege may be required", + (errno == EPERM) ? "EPERM" : "EACCES"); + } else { + err = PCAP_ERROR_PERM_DENIED; + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Attempt to set %s mode failed with %s - root privilege may be required", + (level == DL_PROMISC_MULTI) ? "multicast" : "SAP promiscuous", + (errno == EPERM) ? "EPERM" : "EACCES"); + } + } else { err = PCAP_ERROR; - pcap_libdlpi_err(p->opt.device, "dlpi_promiscon" STRINGIFY(level), - retv, p->errbuf); + pcap_libdlpi_err(p->opt.device, + "dlpi_promiscon" STRINGIFY(level), + retv, p->errbuf); + } return (err); } return (0); @@ -427,7 +449,7 @@ process_pkts: } static int -pcap_inject_libdlpi(pcap_t *p, const void *buf, size_t size) +pcap_inject_libdlpi(pcap_t *p, const void *buf, int size) { struct pcap_dlpi *pd = p->priv; int retv; @@ -468,7 +490,7 @@ pcap_cleanup_libdlpi(pcap_t *p) static void pcap_libdlpi_err(const char *linkname, const char *func, int err, char *errbuf) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "libpcap: %s failed on %s: %s", + snprintf(errbuf, PCAP_ERRBUF_SIZE, "libpcap: %s failed on %s: %s", func, linkname, dlpi_strerror(err)); } @@ -477,7 +499,7 @@ pcap_create_interface(const char *device _U_, char *ebuf) { pcap_t *p; - p = pcap_create_common(ebuf, sizeof (struct pcap_dlpi)); + p = PCAP_CREATE_COMMON(ebuf, struct pcap_dlpi); if (p == NULL) return (NULL); diff --git a/pcap-linktype.manmisc.in b/pcap-linktype.manmisc.in index 777e9dc73d7d..736a91c71453 100644 --- a/pcap-linktype.manmisc.in +++ b/pcap-linktype.manmisc.in @@ -18,7 +18,7 @@ .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" -.TH PCAP-LINKTYPE @MAN_MISC_INFO@ "7 April 2014" +.TH PCAP-LINKTYPE @MAN_MISC_INFO@ "6 April 2020" .SH NAME pcap-linktype \- link-layer header types supported by libpcap .SH DESCRIPTION @@ -38,11 +38,11 @@ so they are sometimes called "DLT_ values". The values stored in the link-layer header type field in the savefile header are, in most but not all cases, the same as the values returned by -.BR pcap_datalink() . +.BR pcap_datalink (). The names for those values begin with .BR LINKTYPE_ . .PP The link-layer header types supported by libpcap are described at -https://www.tcpdump.org/linktypes.html. +https://www.tcpdump.org/linktypes.html . .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap-linux.c b/pcap-linux.c index 70334b3c860b..43d2a9f0eec6 100644 --- a/pcap-linux.c +++ b/pcap-linux.c @@ -67,48 +67,6 @@ * SUCH DAMAGE. */ -/* - * Known problems with 2.0[.x] kernels: - * - * - The loopback device gives every packet twice; on 2.2[.x] kernels, - * if we use PF_PACKET, we can filter out the transmitted version - * of the packet by using data in the "sockaddr_ll" returned by - * "recvfrom()", but, on 2.0[.x] kernels, we have to use - * PF_INET/SOCK_PACKET, which means "recvfrom()" supplies a - * "sockaddr_pkt" which doesn't give us enough information to let - * us do that. - * - * - We have to set the interface's IFF_PROMISC flag ourselves, if - * we're to run in promiscuous mode, which means we have to turn - * it off ourselves when we're done; the kernel doesn't keep track - * of how many sockets are listening promiscuously, which means - * it won't get turned off automatically when no sockets are - * listening promiscuously. We catch "pcap_close()" and, for - * interfaces we put into promiscuous mode, take them out of - * promiscuous mode - which isn't necessarily the right thing to - * do, if another socket also requested promiscuous mode between - * the time when we opened the socket and the time when we close - * the socket. - * - * - MSG_TRUNC isn't supported, so you can't specify that "recvfrom()" - * return the amount of data that you could have read, rather than - * the amount that was returned, so we can't just allocate a buffer - * whose size is the snapshot length and pass the snapshot length - * as the byte count, and also pass MSG_TRUNC, so that the return - * value tells us how long the packet was on the wire. - * - * This means that, if we want to get the actual size of the packet, - * so we can return it in the "len" field of the packet header, - * we have to read the entire packet, not just the part that fits - * within the snapshot length, and thus waste CPU time copying data - * from the kernel that our caller won't see. - * - * We have to get the actual size, and supply it in "len", because - * otherwise, the IP dissector in tcpdump, for example, will complain - * about "truncated-ip", as the packet will appear to have been - * shorter, on the wire, than the IP header said it should have been. - */ - #define _GNU_SOURCE @@ -119,7 +77,6 @@ #include #include #include -#include #include #include #include @@ -132,113 +89,69 @@ #include #include #include +#include #include #include -#include +#include #include #include +#include #include "pcap-int.h" #include "pcap/sll.h" #include "pcap/vlan.h" +#include "pcap/can_socketcan.h" + +#include "diag-control.h" /* - * If PF_PACKET is defined, we can use {SOCK_RAW,SOCK_DGRAM}/PF_PACKET - * sockets rather than SOCK_PACKET sockets. - * - * To use them, we include rather than - * ; we do so because - * - * some Linux distributions (e.g., Slackware 4.0) have 2.2 or - * later kernels and libc5, and don't provide a - * file; - * - * not all versions of glibc2 have a file - * that defines stuff needed for some of the 2.4-or-later-kernel - * features, so if the system has a 2.4 or later kernel, we - * still can't use those features. - * - * We're already including a number of other headers, and - * this code is Linux-specific (no other OS has PF_PACKET sockets as - * a raw packet capture mechanism), so it's not as if you gain any - * useful portability by using - * - * XXX - should we just include even if PF_PACKET - * isn't defined? It only defines one data structure in 2.0.x, so - * it shouldn't cause any problems. + * We require TPACKET_V2 support. */ -#ifdef PF_PACKET -# include +#ifndef TPACKET2_HDRLEN +#error "Libpcap will only work if TPACKET_V2 is supported; you must build for a 2.6.27 or later kernel" +#endif - /* - * On at least some Linux distributions (for example, Red Hat 5.2), - * there's no file, but PF_PACKET is defined if - * you include , but doesn't define - * any of the PF_PACKET stuff such as "struct sockaddr_ll" or any of - * the PACKET_xxx stuff. - * - * So we check whether PACKET_HOST is defined, and assume that we have - * PF_PACKET sockets only if it is defined. - */ -# ifdef PACKET_HOST -# define HAVE_PF_PACKET_SOCKETS -# ifdef PACKET_AUXDATA -# define HAVE_PACKET_AUXDATA -# endif /* PACKET_AUXDATA */ -# endif /* PACKET_HOST */ +/* check for memory mapped access avaibility. 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 +# define HAVE_TPACKET3 +#endif /* TPACKET3_HDRLEN */ +/* + * Not all compilers that are used to compile code to run on Linux have + * these builtins. For example, older versions of GCC don't, and at + * least some people are doing cross-builds for MIPS with older versions + * of GCC. + */ +#ifndef HAVE___ATOMIC_LOAD_N +#define __atomic_load_n(ptr, memory_model) (*(ptr)) +#endif +#ifndef HAVE___ATOMIC_STORE_N +#define __atomic_store_n(ptr, val, memory_model) *(ptr) = (val) +#endif - /* check for memory mapped access avaibility. We assume every needed - * struct is defined if the macro TPACKET_HDRLEN is defined, because it - * uses many ring related structs and macros */ -# ifdef PCAP_SUPPORT_PACKET_RING -# ifdef TPACKET_HDRLEN -# define HAVE_PACKET_RING -# ifdef TPACKET3_HDRLEN -# define HAVE_TPACKET3 -# endif /* TPACKET3_HDRLEN */ -# ifdef TPACKET2_HDRLEN -# define HAVE_TPACKET2 -# else /* TPACKET2_HDRLEN */ -# define TPACKET_V1 0 /* Old kernel with only V1, so no TPACKET_Vn defined */ -# endif /* TPACKET2_HDRLEN */ -# endif /* TPACKET_HDRLEN */ -# endif /* PCAP_SUPPORT_PACKET_RING */ -#endif /* PF_PACKET */ +#define packet_mmap_acquire(pkt) \ + (__atomic_load_n(&pkt->tp_status, __ATOMIC_ACQUIRE) != TP_STATUS_KERNEL) +#define packet_mmap_release(pkt) \ + (__atomic_store_n(&pkt->tp_status, TP_STATUS_KERNEL, __ATOMIC_RELEASE)) +#define packet_mmap_v3_acquire(pkt) \ + (__atomic_load_n(&pkt->hdr.bh1.block_status, __ATOMIC_ACQUIRE) != TP_STATUS_KERNEL) +#define packet_mmap_v3_release(pkt) \ + (__atomic_store_n(&pkt->hdr.bh1.block_status, TP_STATUS_KERNEL, __ATOMIC_RELEASE)) -#ifdef SO_ATTACH_FILTER #include #include -#endif #ifdef HAVE_LINUX_NET_TSTAMP_H #include #endif -#ifdef HAVE_LINUX_SOCKIOS_H -#include -#endif - -#ifdef HAVE_LINUX_IF_BONDING_H +/* + * For checking whether a device is a bonding device. + */ #include -/* - * The ioctl code to use to check whether a device is a bonding device. - */ -#if defined(SIOCBONDINFOQUERY) - #define BOND_INFO_QUERY_IOCTL SIOCBONDINFOQUERY -#elif defined(BOND_INFO_QUERY_OLD) - #define BOND_INFO_QUERY_IOCTL BOND_INFO_QUERY_OLD -#endif -#endif /* HAVE_LINUX_IF_BONDING_H */ - -/* - * Got Wireless Extensions? - */ -#ifdef HAVE_LINUX_WIRELESS_H -#include -#endif /* HAVE_LINUX_WIRELESS_H */ - /* * Got libnl? */ @@ -252,39 +165,10 @@ #include #endif /* HAVE_LIBNL */ -/* - * Got ethtool support? - */ -#ifdef HAVE_LINUX_ETHTOOL_H -#include -#endif - #ifndef HAVE_SOCKLEN_T typedef int socklen_t; #endif -#ifndef MSG_TRUNC -/* - * This is being compiled on a system that lacks MSG_TRUNC; define it - * with the value it has in the 2.2 and later kernels, so that, on - * those kernels, when we pass it in the flags argument to "recvfrom()" - * we're passing the right value and thus get the MSG_TRUNC behavior - * we want. (We don't get that behavior on 2.0[.x] kernels, because - * they didn't support MSG_TRUNC.) - */ -#define MSG_TRUNC 0x20 -#endif - -#ifndef SOL_PACKET -/* - * This is being compiled on a system that lacks SOL_PACKET; define it - * with the value it has in the 2.2 and later kernels, so that we can - * set promiscuous mode in the good modern way rather than the old - * 2.0-kernel crappy way. - */ -#define SOL_PACKET 263 -#endif - #define MAX_LINKHEADER_SIZE 256 /* @@ -295,11 +179,10 @@ typedef int socklen_t; #define BIGGER_THAN_ALL_MTUS (64*1024) /* - * Private data for capturing on Linux SOCK_PACKET or PF_PACKET sockets. + * Private data for capturing on Linux PF_PACKET sockets. */ struct pcap_linux { - u_int packets_read; /* count of packets read with recvfrom() */ - long proc_dropped; /* packets reported dropped by /proc/net/dev */ + long long sysfs_dropped; /* packets reported dropped by /sys/class/net/{if_name}/statistics/rx_{missed,fifo}_errors */ struct pcap_stat stat; char *device; /* device name */ @@ -307,10 +190,10 @@ struct pcap_linux { int blocks_to_filter_in_userland; int must_do_on_close; /* stuff we must do when we close */ int timeout; /* timeout for buffering */ - int sock_packet; /* using Linux 2.0 compatible interface */ int cooked; /* using SOCK_DGRAM rather than SOCK_RAW */ int ifindex; /* interface index of device we're bound to */ int lo_ifindex; /* interface index of the loopback device */ + int netdown; /* we got an ENETDOWN and haven't resolved it */ bpf_u_int32 oldmode; /* mode to restore when turning monitor mode off */ char *mondevice; /* mac80211 monitor device we created */ u_char *mmapbuf; /* memory-mapped region pointer */ @@ -324,90 +207,54 @@ struct pcap_linux { unsigned char *current_packet; /* Current packet within the TPACKET_V3 block. Move to next block if NULL. */ int packets_left; /* Unhandled packets left within the block from previous call to pcap_read_linux_mmap_v3 in case of TPACKET_V3. */ #endif + int poll_breakloop_fd; /* fd to an eventfd to break from blocking operations */ }; /* * Stuff to do when we close. */ -#define MUST_CLEAR_PROMISC 0x00000001 /* clear promiscuous mode */ -#define MUST_CLEAR_RFMON 0x00000002 /* clear rfmon (monitor) mode */ -#define MUST_DELETE_MONIF 0x00000004 /* delete monitor-mode interface */ +#define MUST_CLEAR_RFMON 0x00000001 /* clear rfmon (monitor) mode */ +#define MUST_DELETE_MONIF 0x00000002 /* delete monitor-mode interface */ /* * Prototypes for internal functions and methods. */ 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); +static int is_wifi(const char *); +static void map_arphrd_to_dlt(pcap_t *, int, const char *, int); static int pcap_activate_linux(pcap_t *); -static int activate_old(pcap_t *); -static int activate_new(pcap_t *); -static int activate_mmap(pcap_t *, int *); +static int setup_socket(pcap_t *, int); +static int setup_mmapped(pcap_t *, int *); static int pcap_can_set_rfmon_linux(pcap_t *); -static int pcap_read_linux(pcap_t *, int, pcap_handler, u_char *); -static int pcap_read_packet(pcap_t *, pcap_handler, u_char *); -static int pcap_inject_linux(pcap_t *, const void *, size_t); +static int pcap_inject_linux(pcap_t *, const void *, int); static int pcap_stats_linux(pcap_t *, struct pcap_stat *); static int pcap_setfilter_linux(pcap_t *, struct bpf_program *); 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 *); -/* - * 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; -}; - -/* - * 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 { - struct tpacket_hdr *h1; - struct tpacket_hdr_64 *h1_64; -#ifdef HAVE_TPACKET2 struct tpacket2_hdr *h2; -#endif #ifdef HAVE_TPACKET3 struct tpacket_block_desc *h3; #endif - void *raw; + u_char *raw; }; -#ifdef HAVE_PACKET_RING -#define RING_GET_FRAME_AT(h, offset) (((union thdr **)h->buffer)[(offset)]) +#define RING_GET_FRAME_AT(h, offset) (((u_char **)h->buffer)[(offset)]) #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 prepare_tpacket_socket(pcap_t *handle); -static void pcap_cleanup_linux_mmap(pcap_t *); -static int pcap_read_linux_mmap_v1(pcap_t *, int, pcap_handler , u_char *); -static int pcap_read_linux_mmap_v1_64(pcap_t *, int, pcap_handler , u_char *); -#ifdef HAVE_TPACKET2 static int pcap_read_linux_mmap_v2(pcap_t *, int, pcap_handler , u_char *); -#endif #ifdef HAVE_TPACKET3 static int pcap_read_linux_mmap_v3(pcap_t *, int, pcap_handler , u_char *); #endif -static int pcap_setfilter_linux_mmap(pcap_t *, struct bpf_program *); -static int pcap_setnonblock_mmap(pcap_t *p, int nonblock); -static int pcap_getnonblock_mmap(pcap_t *p); -static void pcap_oneshot_mmap(u_char *user, const struct pcap_pkthdr *h, +static int pcap_setnonblock_linux(pcap_t *p, int nonblock); +static int pcap_getnonblock_linux(pcap_t *p); +static void pcap_oneshot_linux(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes); -#endif /* * In pre-3.0 kernels, the tp_vlan_tci field is set to whatever the @@ -455,34 +302,28 @@ static void pcap_oneshot_mmap(u_char *user, const struct pcap_pkthdr *h, # define VLAN_TPID(hdr, hv) ETH_P_8021Q #endif +/* + * Required select timeout if we're polling for an "interface disappeared" + * indication - 1 millisecond. + */ +static const struct timeval netdown_timeout = { + 0, 1000 /* 1000 microseconds = 1 millisecond */ +}; + /* * Wrap some ioctl calls */ -#ifdef HAVE_PF_PACKET_SOCKETS static int iface_get_id(int fd, const char *device, char *ebuf); -#endif /* HAVE_PF_PACKET_SOCKETS */ static int iface_get_mtu(int fd, const char *device, char *ebuf); static int iface_get_arptype(int fd, const char *device, char *ebuf); -#ifdef HAVE_PF_PACKET_SOCKETS static int iface_bind(int fd, int ifindex, char *ebuf, int protocol); -#ifdef IW_MODE_MONITOR -static int has_wext(int sock_fd, const char *device, char *ebuf); -#endif /* IW_MODE_MONITOR */ static int enter_rfmon_mode(pcap_t *handle, int sock_fd, const char *device); -#endif /* HAVE_PF_PACKET_SOCKETS */ -#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 -#ifdef HAVE_PACKET_RING static int iface_get_offload(pcap_t *handle); -#endif -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_program(pcap_t *handle, struct sock_fprog *fcode); 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); @@ -491,31 +332,29 @@ static struct sock_filter total_insn = BPF_STMT(BPF_RET | BPF_K, 0); static struct sock_fprog total_fcode = { 1, &total_insn }; -#endif /* SO_ATTACH_FILTER */ + +static int iface_dsa_get_proto_info(const char *device, pcap_t *handle); pcap_t * pcap_create_interface(const char *device, char *ebuf) { pcap_t *handle; - handle = pcap_create_common(ebuf, sizeof (struct pcap_linux)); + handle = PCAP_CREATE_COMMON(ebuf, struct pcap_linux); if (handle == NULL) return NULL; 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 -#if defined(SIOCGSTAMPNS) && defined(SO_TIMESTAMPNS) /* * We claim that we support microsecond and nanosecond time * stamps. @@ -524,7 +363,6 @@ pcap_create_interface(const char *device, char *ebuf) * microsecond or nanosecond time stamps on arbitrary * adapters? */ - handle->tstamp_precision_count = 2; handle->tstamp_precision_list = malloc(2 * sizeof(u_int)); if (handle->tstamp_precision_list == NULL) { pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, @@ -534,16 +372,19 @@ pcap_create_interface(const char *device, char *ebuf) } handle->tstamp_precision_list[0] = PCAP_TSTAMP_PRECISION_MICRO; handle->tstamp_precision_list[1] = PCAP_TSTAMP_PRECISION_NANO; -#endif /* defined(SIOCGSTAMPNS) && defined(SO_TIMESTAMPNS) */ + handle->tstamp_precision_count = 2; + + struct pcap_linux *handlep = handle->priv; + handlep->poll_breakloop_fd = eventfd(0, EFD_NONBLOCK); return handle; } #ifdef HAVE_LIBNL /* - * If interface {if} is a mac80211 driver, the file - * /sys/class/net/{if}/phy80211 is a symlink to - * /sys/class/ieee80211/{phydev}, for some {phydev}. + * If interface {if_name} is a mac80211 driver, the file + * /sys/class/net/{if_name}/phy80211 is a symlink to + * /sys/class/ieee80211/{phydev_name}, for some {phydev_name}. * * On Fedora 9, with a 2.6.26.3-29 kernel, my Zydas stick, at * least, has a "wmaster0" device and a "wlan0" device; the @@ -554,24 +395,30 @@ pcap_create_interface(const char *device, char *ebuf) * airmon-ng searches through /sys/class/net for devices named * monN, starting with mon0; as soon as one *doesn't* exist, * it chooses that as the monitor device name. If the "iw" - * command exists, it does "iw dev {if} interface add {monif} - * type monitor", where {monif} is the monitor device. It - * then (sigh) sleeps .1 second, and then configures the - * device up. Otherwise, if /sys/class/ieee80211/{phydev}/add_iface - * is a file, it writes {mondev}, without a newline, to that file, - * and again (sigh) sleeps .1 second, and then iwconfig's that - * device into monitor mode and configures it up. Otherwise, - * you can't do monitor mode. + * command exists, it does + * + * iw dev {if_name} interface add {monif_name} type monitor + * + * where {monif_name} is the monitor device. It then (sigh) sleeps + * .1 second, and then configures the device up. Otherwise, if + * /sys/class/ieee80211/{phydev_name}/add_iface is a file, it writes + * {mondev_name}, without a newline, to that file, and again (sigh) + * sleeps .1 second, and then iwconfig's that device into monitor + * mode and configures it up. Otherwise, you can't do monitor mode. * * All these devices are "glued" together by having the - * /sys/class/net/{device}/phy80211 links pointing to the same + * /sys/class/net/{if_name}/phy80211 links pointing to the same * place, so, given a wmaster, wlan, or mon device, you can * find the other devices by looking for devices with * the same phy80211 link. * * To turn monitor mode off, delete the monitor interface, - * either with "iw dev {monif} interface del" or by sending - * {monif}, with no NL, down /sys/class/ieee80211/{phydev}/remove_iface + * either with + * + * iw dev {monif_name} interface del + * + * or by sending {monif_name}, with no NL, down + * /sys/class/ieee80211/{phydev_name}/remove_iface * * Note: if you try to create a monitor device named "monN", and * there's already a "monN" device, it fails, as least with @@ -599,7 +446,7 @@ get_mac80211_phydev(pcap_t *handle, const char *device, char *phydev_path, * Generate the path string for the symlink to the physical device. */ if (asprintf(&pathstr, "/sys/class/net/%s/phy80211", device) == -1) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: Can't generate path name string for /sys/class/net device", device); return PCAP_ERROR; @@ -624,39 +471,6 @@ get_mac80211_phydev(pcap_t *handle, const char *device, char *phydev_path, return 1; } -#ifdef HAVE_LIBNL_SOCKETS -#define get_nl_errmsg nl_geterror -#else -/* libnl 2.x compatibility code */ - -#define nl_sock nl_handle - -static inline struct nl_handle * -nl_socket_alloc(void) -{ - return nl_handle_alloc(); -} - -static inline void -nl_socket_free(struct nl_handle *h) -{ - nl_handle_destroy(h); -} - -#define get_nl_errmsg strerror - -static inline int -__genl_ctrl_alloc_cache(struct nl_handle *h, struct nl_cache **cache) -{ - struct nl_cache *tmp = genl_ctrl_alloc_cache(h); - if (!tmp) - return -ENOMEM; - *cache = tmp; - return 0; -} -#define genl_ctrl_alloc_cache __genl_ctrl_alloc_cache -#endif /* !HAVE_LIBNL_SOCKETS */ - struct nl80211_state { struct nl_sock *nl_sock; struct nl_cache *nl_cache; @@ -670,28 +484,28 @@ nl80211_init(pcap_t *handle, struct nl80211_state *state, const char *device) state->nl_sock = nl_socket_alloc(); if (!state->nl_sock) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: failed to allocate netlink handle", device); return PCAP_ERROR; } if (genl_connect(state->nl_sock)) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: failed to connect to generic netlink", device); goto out_handle_destroy; } err = genl_ctrl_alloc_cache(state->nl_sock, &state->nl_cache); if (err < 0) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: failed to allocate generic netlink cache: %s", - device, get_nl_errmsg(-err)); + device, nl_geterror(-err)); goto out_handle_destroy; } state->nl80211 = genl_ctrl_search_by_name(state->nl_cache, "nl80211"); if (!state->nl80211) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: nl80211 not found", device); goto out_cache_free; } @@ -732,7 +546,7 @@ add_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state, msg = nlmsg_alloc(); if (!msg) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: failed to allocate netlink msg", device); return PCAP_ERROR; } @@ -740,16 +554,14 @@ add_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state, genlmsg_put(msg, 0, 0, genl_family_get_id(state->nl80211), 0, 0, NL80211_CMD_NEW_INTERFACE, 0); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); +DIAG_OFF_NARROWING NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, mondevice); +DIAG_ON_NARROWING NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_MONITOR); err = nl_send_auto_complete(state->nl_sock, msg); if (err < 0) { -#if defined HAVE_LIBNL_NLE if (err == -NLE_FAILURE) { -#else - if (err == -ENFILE) { -#endif /* * Device not available; our caller should just * keep trying. (libnl 2.x maps ENFILE to @@ -764,20 +576,16 @@ add_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state, * Real failure, not just "that device is not * available. */ - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: nl_send_auto_complete failed adding %s interface: %s", - device, mondevice, get_nl_errmsg(-err)); + device, mondevice, nl_geterror(-err)); nlmsg_free(msg); return PCAP_ERROR; } } err = nl_wait_for_ack(state->nl_sock); if (err < 0) { -#if defined HAVE_LIBNL_NLE if (err == -NLE_FAILURE) { -#else - if (err == -ENFILE) { -#endif /* * Device not available; our caller should just * keep trying. (libnl 2.x maps ENFILE to @@ -792,9 +600,9 @@ add_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state, * Real failure, not just "that device is not * available. */ - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: nl_wait_for_ack failed adding %s interface: %s", - device, mondevice, get_nl_errmsg(-err)); + device, mondevice, nl_geterror(-err)); nlmsg_free(msg); return PCAP_ERROR; } @@ -821,7 +629,7 @@ add_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state, return 1; nla_put_failure: - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: nl_put failed adding %s interface", device, mondevice); nlmsg_free(msg); @@ -842,7 +650,7 @@ del_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state, msg = nlmsg_alloc(); if (!msg) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: failed to allocate netlink msg", device); return PCAP_ERROR; } @@ -853,17 +661,17 @@ del_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state, err = nl_send_auto_complete(state->nl_sock, msg); if (err < 0) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: nl_send_auto_complete failed deleting %s interface: %s", - device, mondevice, get_nl_errmsg(-err)); + device, mondevice, nl_geterror(-err)); nlmsg_free(msg); return PCAP_ERROR; } err = nl_wait_for_ack(state->nl_sock); if (err < 0) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: nl_wait_for_ack failed adding %s interface: %s", - device, mondevice, get_nl_errmsg(-err)); + device, mondevice, nl_geterror(-err)); nlmsg_free(msg); return PCAP_ERROR; } @@ -875,177 +683,14 @@ del_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state, return 1; nla_put_failure: - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: nl_put failed deleting %s interface", device, mondevice); nlmsg_free(msg); return PCAP_ERROR; } - -static int -enter_rfmon_mode_mac80211(pcap_t *handle, int sock_fd, const char *device) -{ - struct pcap_linux *handlep = handle->priv; - int ret; - char phydev_path[PATH_MAX+1]; - struct nl80211_state nlstate; - struct ifreq ifr; - u_int n; - - /* - * Is this a mac80211 device? - */ - ret = get_mac80211_phydev(handle, device, phydev_path, PATH_MAX); - if (ret < 0) - return ret; /* error */ - if (ret == 0) - return 0; /* no error, but not mac80211 device */ - - /* - * XXX - is this already a monN device? - * If so, we're done. - * Is that determined by old Wireless Extensions ioctls? - */ - - /* - * OK, it's apparently a mac80211 device. - * Try to find an unused monN device for it. - */ - ret = nl80211_init(handle, &nlstate, device); - if (ret != 0) - return ret; - for (n = 0; n < UINT_MAX; n++) { - /* - * Try mon{n}. - */ - char mondevice[3+10+1]; /* mon{UINT_MAX}\0 */ - - pcap_snprintf(mondevice, sizeof mondevice, "mon%u", n); - ret = add_mon_if(handle, sock_fd, &nlstate, device, mondevice); - if (ret == 1) { - /* - * Success. We don't clean up the libnl state - * yet, as we'll be using it later. - */ - goto added; - } - if (ret < 0) { - /* - * Hard failure. Just return ret; handle->errbuf - * has already been set. - */ - nl80211_cleanup(&nlstate); - return ret; - } - } - - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, - "%s: No free monN interfaces", device); - nl80211_cleanup(&nlstate); - return PCAP_ERROR; - -added: - -#if 0 - /* - * Sleep for .1 seconds. - */ - delay.tv_sec = 0; - delay.tv_nsec = 500000000; - nanosleep(&delay, NULL); -#endif - - /* - * If we haven't already done so, arrange to have - * "pcap_close_all()" called when we exit. - */ - if (!pcap_do_addexit(handle)) { - /* - * "atexit()" failed; don't put the interface - * in rfmon mode, just give up. - */ - del_mon_if(handle, sock_fd, &nlstate, device, - handlep->mondevice); - nl80211_cleanup(&nlstate); - return PCAP_ERROR; - } - - /* - * Now configure the monitor interface up. - */ - memset(&ifr, 0, sizeof(ifr)); - 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, - handlep->mondevice); - del_mon_if(handle, sock_fd, &nlstate, device, - handlep->mondevice); - nl80211_cleanup(&nlstate); - return PCAP_ERROR; - } - ifr.ifr_flags |= IFF_UP|IFF_RUNNING; - if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1) { - pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, - errno, "%s: Can't set flags for %s", device, - handlep->mondevice); - del_mon_if(handle, sock_fd, &nlstate, device, - handlep->mondevice); - nl80211_cleanup(&nlstate); - return PCAP_ERROR; - } - - /* - * Success. Clean up the libnl state. - */ - nl80211_cleanup(&nlstate); - - /* - * Note that we have to delete the monitor device when we close - * the handle. - */ - handlep->must_do_on_close |= MUST_DELETE_MONIF; - - /* - * Add this to the list of pcaps to close when we exit. - */ - pcap_add_to_pcaps_to_close(handle); - - return 1; -} #endif /* HAVE_LIBNL */ -#ifdef IW_MODE_MONITOR -/* - * Bonding devices mishandle unknown ioctls; they fail with ENODEV - * rather than ENOTSUP, EOPNOTSUPP, or ENOTTY, so Wireless Extensions - * will fail with ENODEV if we try to do them on a bonding device, - * making us return a "no such device" indication rather than just - * saying "no Wireless Extensions". - * - * So we check for bonding devices, if we can, before trying those - * ioctls, by trying a bonding device information query ioctl to see - * whether it succeeds. - */ -static int -is_bonding_device(int fd, const char *device) -{ -#ifdef BOND_INFO_QUERY_IOCTL - struct ifreq ifr; - ifbond ifb; - - memset(&ifr, 0, sizeof ifr); - 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) - return 1; /* success, so it's a bonding device */ -#endif /* BOND_INFO_QUERY_IOCTL */ - - return 0; /* no, it's not a bonding device */ -} -#endif /* IW_MODE_MONITOR */ - static int pcap_protocol(pcap_t *handle) { int protocol; @@ -1064,10 +709,6 @@ pcap_can_set_rfmon_linux(pcap_t *handle) char phydev_path[PATH_MAX+1]; int ret; #endif -#ifdef IW_MODE_MONITOR - int sock_fd; - struct iwreq ireq; -#endif if (strcmp(handle->opt.device, "any") == 0) { /* @@ -1083,11 +724,6 @@ pcap_can_set_rfmon_linux(pcap_t *handle) * we'll just check whether the device appears to be a * mac80211 device and, if so, assume the device supports * monitor mode. - * - * wmaster devices don't appear to support the Wireless - * Extensions, but we can create a mon device for a - * wmaster device, so we don't bother checking whether - * a mac80211 device supports the Wireless Extensions. */ ret = get_mac80211_phydev(handle, handle->opt.device, phydev_path, PATH_MAX); @@ -1097,229 +733,69 @@ pcap_can_set_rfmon_linux(pcap_t *handle) return 1; /* mac80211 device */ #endif -#ifdef IW_MODE_MONITOR - /* - * Bleah. There doesn't appear to be an ioctl to use to ask - * whether a device supports monitor mode; we'll just do - * SIOCGIWMODE and, if it succeeds, assume the device supports - * monitor mode. - * - * Open a socket on which to attempt to get the mode. - * (We assume that if we have Wireless Extensions support - * we also have PF_PACKET support.) - */ - sock_fd = socket(PF_PACKET, SOCK_RAW, pcap_protocol(handle)); - if (sock_fd == -1) { - pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, - errno, "socket"); - return PCAP_ERROR; - } - - if (is_bonding_device(sock_fd, handle->opt.device)) { - /* It's a bonding device, so don't even try. */ - close(sock_fd); - return 0; - } - - /* - * Attempt to get the current mode. - */ - pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, handle->opt.device, - sizeof ireq.ifr_ifrn.ifrn_name); - if (ioctl(sock_fd, SIOCGIWMODE, &ireq) != -1) { - /* - * Well, we got the mode; assume we can set it. - */ - close(sock_fd); - return 1; - } - if (errno == ENODEV) { - /* The device doesn't even exist. */ - pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, - errno, "SIOCGIWMODE failed"); - close(sock_fd); - return PCAP_ERROR_NO_SUCH_DEVICE; - } - close(sock_fd); -#endif return 0; } /* - * Grabs the number of dropped packets by the interface from /proc/net/dev. + * Grabs the number of missed packets by the interface from + * /sys/class/net/{if_name}/statistics/rx_{missed,fifo}_errors. * - * XXX - what about /sys/class/net/{interface name}/rx_*? There are - * individual devices giving, in ASCII, various rx_ and tx_ statistics. - * - * Or can we get them in binary form from netlink? + * 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. */ -static long int -linux_if_drops(const char * if_name) -{ - char buffer[512]; - FILE *file; - char *bufptr, *nameptr, *colonptr; - int field_to_convert = 3; - long int dropped_pkts = 0; +static long long int +linux_get_stat(const char * if_name, const char * stat) { + ssize_t bytes_read; + int fd; + char buffer[PATH_MAX]; - file = fopen("/proc/net/dev", "r"); - if (!file) + snprintf(buffer, sizeof(buffer), "/sys/class/net/%s/statistics/%s", if_name, stat); + fd = open(buffer, O_RDONLY); + if (fd == -1) return 0; - 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 - grab the third field. */ - if (field_to_convert != 4 && strstr(buffer, "bytes")) - { - field_to_convert = 4; - continue; - } + bytes_read = read(fd, buffer, sizeof(buffer) - 1); + close(fd); + if (bytes_read == -1) + return 0; + buffer[bytes_read] = '\0'; - /* - * 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) - { - /* - * OK, this line has the statistics for the interface. - * Skip past the interface name. - */ - bufptr = colonptr + 1; + return strtoll(buffer, NULL, 10); +} - /* grab the nth field from it */ - while (--field_to_convert && *bufptr != '\0') - { - /* - * This isn't the field we want. - * First, skip any leading blanks before - * the field. - */ - while (*bufptr == ' ') - bufptr++; - - /* - * Now skip the non-blank characters of - * the field. - */ - while (*bufptr != '\0' && *bufptr != ' ') - bufptr++; - } - - 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; - } - } - - fclose(file); - return dropped_pkts; +static long long int +linux_if_drops(const char * if_name) +{ + long long int missed = linux_get_stat(if_name, "rx_missed_errors"); + long long int fifo = linux_get_stat(if_name, "rx_fifo_errors"); + return missed + fifo; } /* - * With older kernels promiscuous mode is kind of interesting because we - * have to reset the interface before exiting. The problem can't really - * be solved without some daemon taking care of managing usage counts. - * If we put the interface into promiscuous mode, we set a flag indicating - * that we must take it out of that mode when the interface is closed, - * and, when closing the interface, if that flag is set we take it out - * of promiscuous mode. - * - * Even with newer kernels, we have the same issue with rfmon mode. + * Monitor mode is kind of interesting because we have to reset the + * interface before exiting. The problem can't really be solved without + * some daemon taking care of managing usage counts. If we put the + * interface into monitor mode, we set a flag indicating that we must + * take it out of that mode when the interface is closed, and, when + * closing the interface, if that flag is set we take it out of monitor + * mode. */ static void pcap_cleanup_linux( pcap_t *handle ) { struct pcap_linux *handlep = handle->priv; - struct ifreq ifr; #ifdef HAVE_LIBNL struct nl80211_state nlstate; int ret; #endif /* HAVE_LIBNL */ -#ifdef IW_MODE_MONITOR - int oldflags; - struct iwreq ireq; -#endif /* IW_MODE_MONITOR */ if (handlep->must_do_on_close != 0) { /* * There's something we have to do when closing this * pcap_t. */ - if (handlep->must_do_on_close & MUST_CLEAR_PROMISC) { - /* - * We put the interface into promiscuous mode; - * take it out of promiscuous mode. - * - * XXX - if somebody else wants it in promiscuous - * mode, this code cannot know that, so it'll take - * it out of promiscuous mode. That's not fixable - * in 2.0[.x] kernels. - */ - memset(&ifr, 0, sizeof(ifr)); - pcap_strlcpy(ifr.ifr_name, handlep->device, - sizeof(ifr.ifr_name)); - if (ioctl(handle->fd, SIOCGIFFLAGS, &ifr) == -1) { - fprintf(stderr, - "Can't restore interface %s flags (SIOCGIFFLAGS failed: %s).\n" - "Please adjust manually.\n" - "Hint: This can't happen with Linux >= 2.2.0.\n", - handlep->device, strerror(errno)); - } else { - if (ifr.ifr_flags & IFF_PROMISC) { - /* - * Promiscuous mode is currently on; - * turn it off. - */ - ifr.ifr_flags &= ~IFF_PROMISC; - if (ioctl(handle->fd, SIOCSIFFLAGS, - &ifr) == -1) { - fprintf(stderr, - "Can't restore interface %s flags (SIOCSIFFLAGS failed: %s).\n" - "Please adjust manually.\n" - "Hint: This can't happen with Linux >= 2.2.0.\n", - handlep->device, - strerror(errno)); - } - } - } - } - #ifdef HAVE_LIBNL if (handlep->must_do_on_close & MUST_DELETE_MONIF) { ret = nl80211_init(handle, &nlstate, handlep->device); @@ -1337,68 +813,6 @@ static void pcap_cleanup_linux( pcap_t *handle ) } #endif /* HAVE_LIBNL */ -#ifdef IW_MODE_MONITOR - if (handlep->must_do_on_close & MUST_CLEAR_RFMON) { - /* - * We put the interface into rfmon mode; - * take it out of rfmon mode. - * - * XXX - if somebody else wants it in rfmon - * mode, this code cannot know that, so it'll take - * it out of rfmon mode. - */ - - /* - * First, take the interface down if it's up; - * otherwise, we might get EBUSY. - * If we get errors, just drive on and print - * a warning if we can't restore the mode. - */ - oldflags = 0; - memset(&ifr, 0, sizeof(ifr)); - pcap_strlcpy(ifr.ifr_name, handlep->device, - sizeof(ifr.ifr_name)); - if (ioctl(handle->fd, SIOCGIFFLAGS, &ifr) != -1) { - if (ifr.ifr_flags & IFF_UP) { - oldflags = ifr.ifr_flags; - ifr.ifr_flags &= ~IFF_UP; - if (ioctl(handle->fd, SIOCSIFFLAGS, &ifr) == -1) - oldflags = 0; /* didn't set, don't restore */ - } - } - - /* - * Now restore the mode. - */ - 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) { - /* - * Scientist, you've failed. - */ - fprintf(stderr, - "Can't restore interface %s wireless mode (SIOCSIWMODE failed: %s).\n" - "Please adjust manually.\n", - handlep->device, strerror(errno)); - } - - /* - * Now bring the interface back up if we brought - * it down. - */ - if (oldflags != 0) { - ifr.ifr_flags = oldflags; - if (ioctl(handle->fd, SIOCSIFFLAGS, &ifr) == -1) { - fprintf(stderr, - "Can't bring interface %s back up (SIOCSIFFLAGS failed: %s).\n" - "Please adjust manually.\n", - handlep->device, strerror(errno)); - } - } - } -#endif /* IW_MODE_MONITOR */ - /* * Take this pcap out of the list of pcaps for which we * have to take the interface out of some mode. @@ -1406,6 +820,19 @@ static void pcap_cleanup_linux( pcap_t *handle ) pcap_remove_from_pcaps_to_close(handle); } + if (handle->fd != -1) { + /* + * Destroy the ring buffer (assuming we've set it up), + * and unmap it if it's mapped. + */ + destroy_ring(handle); + } + + if (handlep->oneshot_buffer != NULL) { + free(handlep->oneshot_buffer); + handlep->oneshot_buffer = NULL; + } + if (handlep->mondevice != NULL) { free(handlep->mondevice); handlep->mondevice = NULL; @@ -1414,9 +841,52 @@ static void pcap_cleanup_linux( pcap_t *handle ) free(handlep->device); handlep->device = NULL; } + + if (handlep->poll_breakloop_fd != -1) { + close(handlep->poll_breakloop_fd); + handlep->poll_breakloop_fd = -1; + } pcap_cleanup_live_common(handle); } +#ifdef HAVE_TPACKET3 +/* + * Some versions of TPACKET_V3 have annoying bugs/misfeatures + * around which we have to work. Determine if we have those + * problems or not. + * 3.19 is the first release with a fixed version of + * TPACKET_V3. We treat anything before that as + * not having a fixed version; that may really mean + * it has *no* version. + */ +static int has_broken_tpacket_v3(void) +{ + struct utsname utsname; + const char *release; + long major, minor; + int matches, verlen; + + /* No version information, assume broken. */ + if (uname(&utsname) == -1) + return 1; + release = utsname.release; + + /* A malformed version, ditto. */ + matches = sscanf(release, "%ld.%ld%n", &major, &minor, &verlen); + if (matches != 2) + return 1; + if (release[verlen] != '.' && release[verlen] != '\0') + return 1; + + /* OK, a fixed version. */ + if (major > 3 || (major == 3 && minor >= 19)) + return 0; + + /* Too old :( */ + return 1; +} +#endif + /* * Set the timeout to be used in poll() with memory-mapped packet capture. */ @@ -1424,45 +894,7 @@ static void set_poll_timeout(struct pcap_linux *handlep) { #ifdef HAVE_TPACKET3 - struct utsname utsname; - char *version_component, *endp; - int major, minor; - int broken_tpacket_v3 = 1; - - /* - * Some versions of TPACKET_V3 have annoying bugs/misfeatures - * around which we have to work. Determine if we have those - * problems or not. - */ - if (uname(&utsname) == 0) { - /* - * 3.19 is the first release with a fixed version of - * TPACKET_V3. We treat anything before that as - * not haveing a fixed version; that may really mean - * it has *no* version. - */ - version_component = utsname.release; - major = strtol(version_component, &endp, 10); - if (endp != version_component && *endp == '.') { - /* - * OK, that was a valid major version. - * Get the minor version. - */ - version_component = endp + 1; - minor = strtol(version_component, &endp, 10); - if (endp != version_component && - (*endp == '.' || *endp == '\0')) { - /* - * OK, that was a valid minor version. - * Is this 3.19 or newer? - */ - if (major >= 4 || (major == 3 && minor >= 19)) { - /* Yes. TPACKET_V3 works correctly. */ - broken_tpacket_v3 = 0; - } - } - } - } + int broken_tpacket_v3 = has_broken_tpacket_v3(); #endif if (handlep->timeout == 0) { #ifdef HAVE_TPACKET3 @@ -1505,11 +937,55 @@ set_poll_timeout(struct pcap_linux *handlep) } } +static void pcap_breakloop_linux(pcap_t *handle) +{ + pcap_breakloop_common(handle); + struct pcap_linux *handlep = handle->priv; + + uint64_t value = 1; + /* XXX - what if this fails? */ + 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; + } +} + /* * Get a handle for a live capture from the given device. You can * pass NULL as device to get all packages (without link level * information of course). If you pass 1 as promisc the interface - * will be set to promiscous mode (XXX: I think this usage should + * will be set to promiscuous mode (XXX: I think this usage should * be deprecated and functions be added to select that later allow * modification of that values -- Torsten). */ @@ -1518,8 +994,10 @@ pcap_activate_linux(pcap_t *handle) { struct pcap_linux *handlep = handle->priv; const char *device; + int is_any_device; struct ifreq ifr; int status = 0; + int status2 = 0; int ret; device = handle->opt.device; @@ -1536,6 +1014,11 @@ pcap_activate_linux(pcap_t *handle) * 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; } @@ -1551,31 +1034,6 @@ pcap_activate_linux(pcap_t *handle) if (handle->snapshot <= 0 || handle->snapshot > MAXIMUM_SNAPLEN) handle->snapshot = MAXIMUM_SNAPLEN; - handle->inject_op = pcap_inject_linux; - handle->setfilter_op = pcap_setfilter_linux; - handle->setdirection_op = pcap_setdirection_linux; - handle->set_datalink_op = pcap_set_datalink_linux; - handle->getnonblock_op = pcap_getnonblock_fd; - handle->setnonblock_op = pcap_setnonblock_fd; - handle->cleanup_op = pcap_cleanup_linux; - handle->read_op = pcap_read_linux; - handle->stats_op = pcap_stats_linux; - - /* - * The "any" device is a special device which causes us not - * to bind to a particular device and thus to look at all - * devices. - */ - if (strcmp(device, "any") == 0) { - if (handle->opt.promisc) { - handle->opt.promisc = 0; - /* Just a warning. */ - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, - "Promiscuous mode not supported on the \"any\" device"); - status = PCAP_WARNING_PROMISC_NOTSUP; - } - } - handlep->device = strdup(device); if (handlep->device == NULL) { pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, @@ -1584,118 +1042,101 @@ pcap_activate_linux(pcap_t *handle) goto fail; } + /* + * The "any" device is a special device which causes us not + * to bind to a particular device and thus to look at all + * devices. + */ + is_any_device = (strcmp(device, "any") == 0); + if (is_any_device) { + if (handle->opt.promisc) { + handle->opt.promisc = 0; + /* Just a warning. */ + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "Promiscuous mode not supported on the \"any\" device"); + status = PCAP_WARNING_PROMISC_NOTSUP; + } + } + /* copy timeout value */ handlep->timeout = handle->opt.timeout; /* * If we're in promiscuous mode, then we probably want * to see when the interface drops packets too, so get an - * initial count from /proc/net/dev + * initial count from + * /sys/class/net/{if_name}/statistics/rx_{missed,fifo}_errors */ if (handle->opt.promisc) - handlep->proc_dropped = linux_if_drops(handlep->device); + handlep->sysfs_dropped = linux_if_drops(handlep->device); /* - * Current Linux kernels use the protocol family PF_PACKET to - * allow direct access to all packets on the network while - * older kernels had a special socket type SOCK_PACKET to - * implement this feature. - * While this old implementation is kind of obsolete we need - * to be compatible with older kernels for a while so we are - * trying both methods with the newer method preferred. + * If the "any" device is specified, try to open a SOCK_DGRAM. + * Otherwise, open a SOCK_RAW. */ - ret = activate_new(handle); + ret = setup_socket(handle, is_any_device); if (ret < 0) { /* - * Fatal error with the new way; just fail. - * ret has the error return; if it's PCAP_ERROR, - * handle->errbuf has been set appropriately. + * Fatal error; the return value is the error code, + * and handle->errbuf has been set to an appropriate + * error message. */ status = ret; goto fail; } - if (ret == 1) { - /* - * Success. - * Try to use memory-mapped access. - */ - switch (activate_mmap(handle, &status)) { - - case 1: - /* - * We succeeded. status has been - * set to the status to return, - * which might be 0, or might be - * a PCAP_WARNING_ value. - * - * Set the timeout to use in poll() before - * returning. - */ - set_poll_timeout(handlep); - return status; - - case 0: - /* - * Kernel doesn't support it - just continue - * with non-memory-mapped access. - */ - break; - - case -1: - /* - * 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 error status to - * return and, if it's PCAP_ERROR, handle->errbuf - * contains the error message. - */ - goto fail; - } - } - else if (ret == 0) { - /* Non-fatal error; try old way */ - if ((ret = activate_old(handle)) != 1) { - /* - * Both methods to open the packet socket failed. - * Tidy up and report our failure (handle->errbuf - * is expected to be set by the functions above). - */ - status = ret; - goto fail; - } - } - /* - * We set up the socket, but not with memory-mapped access. + * Success. + * Try to set up memory-mapped access. */ - if (handle->opt.buffer_size != 0) { + ret = setup_mmapped(handle, &status); + if (ret == -1) { /* - * Set the socket buffer size to the specified value. + * 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 + * error status to return and, if it's + * PCAP_ERROR, handle->errbuf contains + * the error message. */ - if (setsockopt(handle->fd, SOL_SOCKET, SO_RCVBUF, - &handle->opt.buffer_size, - sizeof(handle->opt.buffer_size)) == -1) { - pcap_fmt_errmsg_for_errno(handle->errbuf, - PCAP_ERRBUF_SIZE, errno, "SO_RCVBUF"); - status = PCAP_ERROR; - goto fail; - } - } - - /* Allocate the buffer */ - - handle->buffer = malloc(handle->bufsize + handle->offset); - if (!handle->buffer) { - pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, - errno, "malloc"); - status = PCAP_ERROR; goto fail; } /* - * "handle->fd" is a socket, so "select()" and "poll()" - * should work on it. + * We succeeded. status has been set to the status to return, + * which might be 0, or might be a PCAP_WARNING_ value. */ + /* + * Now that we have activated the mmap ring, we can + * set the correct protocol. + */ + if ((status2 = iface_bind(handle->fd, handlep->ifindex, + handle->errbuf, pcap_protocol(handle))) != 0) { + status = status2; + goto fail; + } + + handle->inject_op = pcap_inject_linux; + handle->setfilter_op = pcap_setfilter_linux; + handle->setdirection_op = pcap_setdirection_linux; + handle->set_datalink_op = pcap_set_datalink_linux; + handle->setnonblock_op = pcap_setnonblock_linux; + handle->getnonblock_op = pcap_getnonblock_linux; + handle->cleanup_op = pcap_cleanup_linux; + handle->stats_op = pcap_stats_linux; + handle->breakloop_op = pcap_breakloop_linux; + + switch (handlep->tp_version) { + + case TPACKET_V2: + handle->read_op = pcap_read_linux_mmap_v2; + break; +#ifdef HAVE_TPACKET3 + case TPACKET_V3: + handle->read_op = pcap_read_linux_mmap_v3; + break; +#endif + } + handle->oneshot_callback = pcap_oneshot_linux; handle->selectable_fd = handle->fd; return status; @@ -1705,25 +1146,17 @@ fail: return status; } -/* - * Read at most max_packets from the capture stream and call the callback - * for each of them. Returns the number of packets handled or -1 if an - * error occured. - */ -static int -pcap_read_linux(pcap_t *handle, int max_packets _U_, pcap_handler callback, u_char *user) -{ - /* - * Currently, on Linux only one packet is delivered per read, - * so we don't loop. - */ - return pcap_read_packet(handle, callback, user); -} - static int 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; } @@ -1757,9 +1190,12 @@ linux_check_direction(const pcap_t *handle, const struct sockaddr_ll *sll) * easily distinguish packets looped back by the CAN * layer than those received by the CAN layer, so we * eliminate this packet instead. + * + * We check whether this is a CAN or CAN FD frame + * by checking whether the device's hardware type + * is ARPHRD_CAN. */ - if ((sll->sll_protocol == LINUX_SLL_P_CAN || - sll->sll_protocol == LINUX_SLL_P_CANFD) && + if (sll->sll_hatype == ARPHRD_CAN && handle->direction != PCAP_D_OUT) return 0; @@ -1780,434 +1216,83 @@ linux_check_direction(const pcap_t *handle, const struct sockaddr_ll *sll) } /* - * Read a packet from the socket calling the handler provided by - * the user. Returns the number of packets received or -1 if an - * error occured. + * Check whether the device to which the pcap_t is bound still exists. + * We do so by asking what address the socket is bound to, and checking + * whether the ifindex in the address is -1, meaning "that device is gone", + * or some other value, meaning "that device still exists". */ static int -pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata) +device_still_exists(pcap_t *handle) { - struct pcap_linux *handlep = handle->priv; - u_char *bp; - int offset; -#ifdef HAVE_PF_PACKET_SOCKETS - struct sockaddr_ll from; -#else - struct sockaddr from; -#endif -#if defined(HAVE_PACKET_AUXDATA) && defined(HAVE_STRUCT_TPACKET_AUXDATA_TP_VLAN_TCI) - struct iovec iov; - struct msghdr msg; - struct cmsghdr *cmsg; - union { - struct cmsghdr cmsg; - char buf[CMSG_SPACE(sizeof(struct tpacket_auxdata))]; - } cmsg_buf; -#else /* defined(HAVE_PACKET_AUXDATA) && defined(HAVE_STRUCT_TPACKET_AUXDATA_TP_VLAN_TCI) */ - socklen_t fromlen; -#endif /* defined(HAVE_PACKET_AUXDATA) && defined(HAVE_STRUCT_TPACKET_AUXDATA_TP_VLAN_TCI) */ - int packet_len, caplen; - struct pcap_pkthdr pcap_header; - - struct bpf_aux_data aux_data; -#ifdef HAVE_PF_PACKET_SOCKETS - /* - * If this is a cooked device, leave extra room for a - * fake packet header. - */ - if (handlep->cooked) { - if (handle->linktype == DLT_LINUX_SLL2) - offset = SLL2_HDR_LEN; - else - offset = SLL_HDR_LEN; - } else - offset = 0; -#else - /* - * This system doesn't have PF_PACKET sockets, so it doesn't - * support cooked devices. - */ - offset = 0; -#endif + struct pcap_linux *handlep = handle->priv; + struct sockaddr_ll addr; + socklen_t addr_len; /* - * Receive a single packet from the kernel. - * We ignore EINTR, as that might just be due to a signal - * being delivered - if the signal should interrupt the - * loop, the signal handler should call pcap_breakloop() - * to set handle->break_loop (we ignore it on other - * platforms as well). - * We also ignore ENETDOWN, so that we can continue to - * capture traffic if the interface goes down and comes - * back up again; comments in the kernel indicate that - * we'll just block waiting for packets if we try to - * receive from a socket that delivered ENETDOWN, and, - * if we're using a memory-mapped buffer, we won't even - * get notified of "network down" events. + * If handlep->ifindex is -1, the socket isn't bound, meaning + * we're capturing on the "any" device; that device never + * disappears. (It should also never be configured down, so + * we shouldn't even get here, but let's make sure.) */ - bp = (u_char *)handle->buffer + handle->offset; + if (handlep->ifindex == -1) + return (1); /* it's still here */ -#if defined(HAVE_PACKET_AUXDATA) && defined(HAVE_STRUCT_TPACKET_AUXDATA_TP_VLAN_TCI) - msg.msg_name = &from; - msg.msg_namelen = sizeof(from); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = &cmsg_buf; - msg.msg_controllen = sizeof(cmsg_buf); - msg.msg_flags = 0; - - iov.iov_len = handle->bufsize - offset; - iov.iov_base = bp + offset; -#endif /* defined(HAVE_PACKET_AUXDATA) && defined(HAVE_STRUCT_TPACKET_AUXDATA_TP_VLAN_TCI) */ - - do { + /* + * OK, now try to get the address for the socket. + */ + addr_len = sizeof (addr); + if (getsockname(handle->fd, (struct sockaddr *) &addr, &addr_len) == -1) { /* - * Has "pcap_breakloop()" been called? + * Error - report an error and return -1. */ - if (handle->break_loop) { - /* - * Yes - clear the flag that indicates that it has, - * and return PCAP_ERROR_BREAK as an indication that - * we were told to break out of the loop. - */ - handle->break_loop = 0; - return PCAP_ERROR_BREAK; - } - -#if defined(HAVE_PACKET_AUXDATA) && defined(HAVE_STRUCT_TPACKET_AUXDATA_TP_VLAN_TCI) - packet_len = recvmsg(handle->fd, &msg, MSG_TRUNC); -#else /* defined(HAVE_PACKET_AUXDATA) && defined(HAVE_STRUCT_TPACKET_AUXDATA_TP_VLAN_TCI) */ - fromlen = sizeof(from); - packet_len = recvfrom( - handle->fd, bp + offset, - handle->bufsize - offset, MSG_TRUNC, - (struct sockaddr *) &from, &fromlen); -#endif /* defined(HAVE_PACKET_AUXDATA) && defined(HAVE_STRUCT_TPACKET_AUXDATA_TP_VLAN_TCI) */ - } while (packet_len == -1 && errno == EINTR); - - /* Check if an error occured */ - - if (packet_len == -1) { - switch (errno) { - - case EAGAIN: - return 0; /* no packet there */ - - case ENETDOWN: - /* - * The device on which we're capturing went away. - * - * XXX - we should really return - * PCAP_ERROR_IFACE_NOT_UP, but pcap_dispatch() - * etc. aren't defined to return that. - */ - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, - "The interface went down"); - return PCAP_ERROR; - - default: - pcap_fmt_errmsg_for_errno(handle->errbuf, - PCAP_ERRBUF_SIZE, errno, "recvfrom"); - return PCAP_ERROR; - } + pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, + errno, "getsockname failed"); + return (-1); } - -#ifdef HAVE_PF_PACKET_SOCKETS - if (!handlep->sock_packet) { + if (addr.sll_ifindex == -1) { /* - * Unfortunately, there is a window between socket() and - * bind() where the kernel may queue packets from any - * interface. If we're bound to a particular interface, - * discard packets not from that interface. - * - * (If socket filters are supported, we could do the - * same thing we do when changing the filter; however, - * that won't handle packet sockets without socket - * filter support, and it's a bit more complicated. - * It would save some instructions per packet, however.) + * This means the device went away. */ - if (handlep->ifindex != -1 && - from.sll_ifindex != handlep->ifindex) - return 0; - - /* - * Do checks based on packet direction. - * We can only do this if we're using PF_PACKET; the - * address returned for SOCK_PACKET is a "sockaddr_pkt" - * which lacks the relevant packet type information. - */ - if (!linux_check_direction(handle, &from)) - return 0; - } -#endif - -#ifdef HAVE_PF_PACKET_SOCKETS - /* - * If this is a cooked device, fill in the fake packet header. - */ - if (handlep->cooked) { - /* - * Add the length of the fake header to the length - * of packet data we read. - */ - 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; - } + return (0); } /* - * Start out with no VLAN information. + * The device presumably just went down. */ - aux_data.vlan_tag_present = 0; - aux_data.vlan_tag = 0; -#if defined(HAVE_PACKET_AUXDATA) && defined(HAVE_STRUCT_TPACKET_AUXDATA_TP_VLAN_TCI) - if (handlep->vlan_offset != -1) { - for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { - struct tpacket_auxdata *aux; - unsigned int len; - struct vlan_tag *tag; - - if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct tpacket_auxdata)) || - cmsg->cmsg_level != SOL_PACKET || - cmsg->cmsg_type != PACKET_AUXDATA) { - /* - * This isn't a PACKET_AUXDATA auxiliary - * data item. - */ - continue; - } - - aux = (struct tpacket_auxdata *)CMSG_DATA(cmsg); - if (!VLAN_VALID(aux, aux)) { - /* - * There is no VLAN information in the - * auxiliary data. - */ - continue; - } - - len = (u_int)packet_len > iov.iov_len ? iov.iov_len : (u_int)packet_len; - if (len < (u_int)handlep->vlan_offset) - break; - - /* - * Move everything in the header, except the - * type field, down VLAN_TAG_LEN bytes, to - * allow us to insert the VLAN tag between - * that stuff and the type field. - */ - bp -= VLAN_TAG_LEN; - memmove(bp, bp + VLAN_TAG_LEN, handlep->vlan_offset); - - /* - * Now insert the tag. - */ - tag = (struct vlan_tag *)(bp + handlep->vlan_offset); - tag->vlan_tpid = htons(VLAN_TPID(aux, aux)); - tag->vlan_tci = htons(aux->tp_vlan_tci); - - /* - * Save a flag indicating that we have a VLAN tag, - * and the VLAN TCI, to bpf_aux_data struct for - * use by the BPF filter if we're doing the - * filtering in userland. - */ - aux_data.vlan_tag_present = 1; - aux_data.vlan_tag = htons(aux->tp_vlan_tci) & 0x0fff; - - /* - * Add the tag to the packet lengths. - */ - packet_len += VLAN_TAG_LEN; - } - } -#endif /* defined(HAVE_PACKET_AUXDATA) && defined(HAVE_STRUCT_TPACKET_AUXDATA_TP_VLAN_TCI) */ -#endif /* HAVE_PF_PACKET_SOCKETS */ - - /* - * XXX: According to the kernel source we should get the real - * packet len if calling recvfrom with MSG_TRUNC set. It does - * not seem to work here :(, but it is supported by this code - * anyway. - * To be honest the code RELIES on that feature so this is really - * broken with 2.2.x kernels. - * I spend a day to figure out what's going on and I found out - * that the following is happening: - * - * The packet comes from a random interface and the packet_rcv - * hook is called with a clone of the packet. That code inserts - * the packet into the receive queue of the packet socket. - * If a filter is attached to that socket that filter is run - * first - and there lies the problem. The default filter always - * cuts the packet at the snaplen: - * - * # tcpdump -d - * (000) ret #68 - * - * So the packet filter cuts down the packet. The recvfrom call - * says "hey, it's only 68 bytes, it fits into the buffer" with - * the result that we don't get the real packet length. This - * is valid at least until kernel 2.2.17pre6. - * - * We currently handle this by making a copy of the filter - * program, fixing all "ret" instructions with non-zero - * operands to have an operand of MAXIMUM_SNAPLEN so that the - * filter doesn't truncate the packet, and supplying that modified - * filter to the kernel. - */ - - caplen = packet_len; - if (caplen > handle->snapshot) - caplen = handle->snapshot; - - /* Run the packet filter if not using kernel filter */ - if (handlep->filter_in_userland && handle->fcode.bf_insns) { - if (bpf_filter_with_aux_data(handle->fcode.bf_insns, bp, - packet_len, caplen, &aux_data) == 0) { - /* rejected by filter */ - return 0; - } - } - - /* Fill in our own header data */ - - /* get timestamp for this packet */ -#if defined(SIOCGSTAMPNS) && defined(SO_TIMESTAMPNS) - if (handle->opt.tstamp_precision == PCAP_TSTAMP_PRECISION_NANO) { - if (ioctl(handle->fd, SIOCGSTAMPNS, &pcap_header.ts) == -1) { - pcap_fmt_errmsg_for_errno(handle->errbuf, - PCAP_ERRBUF_SIZE, errno, "SIOCGSTAMPNS"); - return PCAP_ERROR; - } - } else -#endif - { - if (ioctl(handle->fd, SIOCGSTAMP, &pcap_header.ts) == -1) { - pcap_fmt_errmsg_for_errno(handle->errbuf, - PCAP_ERRBUF_SIZE, errno, "SIOCGSTAMP"); - return PCAP_ERROR; - } - } - - pcap_header.caplen = caplen; - pcap_header.len = packet_len; - - /* - * Count the packet. - * - * Arguably, we should count them before we check the filter, - * as on many other platforms "ps_recv" counts packets - * handed to the filter rather than packets that passed - * the filter, but if filtering is done in the kernel, we - * can't get a count of packets that passed the filter, - * and that would mean the meaning of "ps_recv" wouldn't - * be the same on all Linux systems. - * - * XXX - it's not the same on all systems in any case; - * ideally, we should have a "get the statistics" call - * that supplies more counts and indicates which of them - * it supplies, so that we supply a count of packets - * handed to the filter only on platforms where that - * information is available. - * - * We count them here even if we can get the packet count - * from the kernel, as we can only determine at run time - * whether we'll be able to get it from the kernel (if - * HAVE_STRUCT_TPACKET_STATS isn't defined, we can't get it from - * the kernel, but if it is defined, the library might - * have been built with a 2.4 or later kernel, but we - * might be running on a 2.2[.x] kernel without Alexey - * Kuznetzov's turbopacket patches, and thus the kernel - * might not be able to supply those statistics). We - * could, I guess, try, when opening the socket, to get - * the statistics, and if we can not increment the count - * here, but it's not clear that always incrementing - * the count is more expensive than always testing a flag - * in memory. - * - * We keep the count in "handlep->packets_read", and use that - * for "ps_recv" if we can't get the statistics from the kernel. - * We do that because, if we *can* get the statistics from - * the kernel, we use "handlep->stat.ps_recv" and - * "handlep->stat.ps_drop" as running counts, as reading the - * statistics from the kernel resets the kernel statistics, - * and if we directly increment "handlep->stat.ps_recv" here, - * that means it will count packets *twice* on systems where - * we can get kernel statistics - once here, and once in - * pcap_stats_linux(). - */ - handlep->packets_read++; - - /* Call the user supplied callback function */ - callback(userdata, &pcap_header, bp); - - return 1; + return (1); } static int -pcap_inject_linux(pcap_t *handle, const void *buf, size_t size) +pcap_inject_linux(pcap_t *handle, const void *buf, int size) { struct pcap_linux *handlep = handle->priv; int ret; -#ifdef HAVE_PF_PACKET_SOCKETS - if (!handlep->sock_packet) { - /* PF_PACKET socket */ - if (handlep->ifindex == -1) { - /* - * We don't support sending on the "any" device. - */ - pcap_strlcpy(handle->errbuf, - "Sending packets isn't supported on the \"any\" device", - PCAP_ERRBUF_SIZE); - return (-1); - } - - if (handlep->cooked) { - /* - * 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? - */ - pcap_strlcpy(handle->errbuf, - "Sending packets isn't supported in cooked mode", - PCAP_ERRBUF_SIZE); - return (-1); - } + if (handlep->ifindex == -1) { + /* + * We don't support sending on the "any" device. + */ + pcap_strlcpy(handle->errbuf, + "Sending packets isn't supported on the \"any\" device", + PCAP_ERRBUF_SIZE); + return (-1); } -#endif - ret = send(handle->fd, buf, size, 0); + if (handlep->cooked) { + /* + * 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? + */ + pcap_strlcpy(handle->errbuf, + "Sending packets isn't supported in cooked mode", + PCAP_ERRBUF_SIZE); + return (-1); + } + + ret = (int)send(handle->fd, buf, size, 0); if (ret == -1) { pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, "send"); @@ -2218,27 +1303,20 @@ pcap_inject_linux(pcap_t *handle, const void *buf, size_t size) /* * Get the statistics for the given packet capture handle. - * Reports the number of dropped packets iff the kernel supports - * the PACKET_STATISTICS "getsockopt()" argument (2.4 and later - * kernels, and 2.2[.x] kernels with Alexey Kuznetzov's turbopacket - * patches); otherwise, that information isn't available, and we lie - * and report 0 as the count of dropped packets. */ static int pcap_stats_linux(pcap_t *handle, struct pcap_stat *stats) { struct pcap_linux *handlep = handle->priv; -#ifdef HAVE_STRUCT_TPACKET_STATS #ifdef HAVE_TPACKET3 /* - * For sockets using TPACKET_V1 or TPACKET_V2, the extra - * stuff at the end of a struct tpacket_stats_v3 will not - * be filled in, and we don't look at it so this is OK even - * for those sockets. In addition, the PF_PACKET socket - * code in the kernel only uses the length parameter to - * compute how much data to copy out and to indicate how - * much data was copied out, so it's OK to base it on the - * size of a struct tpacket_stats. + * For sockets using TPACKET_V2, the extra stuff at the end + * of a struct tpacket_stats_v3 will not be filled in, and + * we don't look at it so this is OK even for those sockets. + * In addition, the PF_PACKET socket code in the kernel only + * uses the length parameter to compute how much data to + * copy out and to indicate how much data was copied out, so + * it's OK to base it on the size of a struct tpacket_stats. * * XXX - it's probably OK, in fact, to just use a * struct tpacket_stats for V3 sockets, as we don't @@ -2249,52 +1327,77 @@ pcap_stats_linux(pcap_t *handle, struct pcap_stat *stats) struct tpacket_stats kstats; #endif /* HAVE_TPACKET3 */ socklen_t len = sizeof (struct tpacket_stats); -#endif /* HAVE_STRUCT_TPACKET_STATS */ - long if_dropped = 0; + long long if_dropped = 0; /* - * To fill in ps_ifdrop, we parse /proc/net/dev for the number + * To fill in ps_ifdrop, we parse + * /sys/class/net/{if_name}/statistics/rx_{missed,fifo}_errors + * for the numbers */ if (handle->opt.promisc) { - if_dropped = handlep->proc_dropped; - handlep->proc_dropped = linux_if_drops(handlep->device); - handlep->stat.ps_ifdrop += (handlep->proc_dropped - if_dropped); + /* + * XXX - is there any reason to do this by remembering + * the last counts value, subtracting it from the + * current counts value, and adding that to stat.ps_ifdrop, + * maintaining stat.ps_ifdrop as a count, rather than just + * saving the *initial* counts value and setting + * stat.ps_ifdrop to the difference between the current + * value and the initial value? + * + * One reason might be to handle the count wrapping + * around, on platforms where the count is 32 bits + * and where you might get more than 2^32 dropped + * packets; is there any other reason? + * + * (We maintain the count as a long long int so that, + * if the kernel maintains the counts as 64-bit even + * on 32-bit platforms, we can handle the real count. + * + * Unfortunately, we can't report 64-bit counts; we + * need a better API for reporting statistics, such as + * one that reports them in a style similar to the + * pcapng Interface Statistics Block, so that 1) the + * counts are 64-bit, 2) it's easier to add new statistics + * without breaking the ABI, and 3) it's easier to + * indicate to a caller that wants one particular + * statistic that it's not available by just not supplying + * it.) + */ + if_dropped = handlep->sysfs_dropped; + handlep->sysfs_dropped = linux_if_drops(handlep->device); + handlep->stat.ps_ifdrop += (u_int)(handlep->sysfs_dropped - if_dropped); } -#ifdef HAVE_STRUCT_TPACKET_STATS /* * Try to get the packet counts from the kernel. */ if (getsockopt(handle->fd, SOL_PACKET, PACKET_STATISTICS, &kstats, &len) > -1) { /* - * On systems where the PACKET_STATISTICS "getsockopt()" - * argument is supported on PF_PACKET sockets: + * "ps_recv" counts only packets that *passed* the + * filter, not packets that didn't pass the filter. + * This includes packets later dropped because we + * ran out of buffer space. * - * "ps_recv" counts only packets that *passed* the - * filter, not packets that didn't pass the filter. - * This includes packets later dropped because we - * ran out of buffer space. + * "ps_drop" counts packets dropped because we ran + * out of buffer space. It doesn't count packets + * dropped by the interface driver. It counts only + * packets that passed the filter. * - * "ps_drop" counts packets dropped because we ran - * out of buffer space. It doesn't count packets - * dropped by the interface driver. It counts only - * packets that passed the filter. + * See above for ps_ifdrop. * - * See above for ps_ifdrop. + * Both statistics include packets not yet read from + * the kernel by libpcap, and thus not yet seen by + * the application. * - * Both statistics include packets not yet read from - * the kernel by libpcap, and thus not yet seen by - * the application. - * - * In "linux/net/packet/af_packet.c", at least in the - * 2.4.9 kernel, "tp_packets" is incremented for every - * packet that passes the packet filter *and* is - * successfully queued on the socket; "tp_drops" is + * In "linux/net/packet/af_packet.c", at least in 2.6.27 + * through 5.6 kernels, "tp_packets" is incremented for + * every packet that passes the packet filter *and* is + * successfully copied to the ring buffer; "tp_drops" is * incremented for every packet dropped because there's - * not enough free space in the socket buffer. + * not enough free space in the ring buffer. * * When the statistics are returned for a PACKET_STATISTICS * "getsockopt()" call, "tp_drops" is added to "tp_packets", @@ -2321,336 +1424,10 @@ pcap_stats_linux(pcap_t *handle, struct pcap_stat *stats) *stats = handlep->stat; return 0; } - else - { - /* - * If the error was EOPNOTSUPP, fall through, so that - * if you build the library on a system with - * "struct tpacket_stats" and run it on a system - * that doesn't, it works as it does if the library - * is built on a system without "struct tpacket_stats". - */ - if (errno != EOPNOTSUPP) { - pcap_fmt_errmsg_for_errno(handle->errbuf, - PCAP_ERRBUF_SIZE, errno, "pcap_stats"); - return -1; - } - } -#endif - /* - * On systems where the PACKET_STATISTICS "getsockopt()" argument - * is not supported on PF_PACKET sockets: - * - * "ps_recv" counts only packets that *passed* the filter, - * not packets that didn't pass the filter. It does not - * count packets dropped because we ran out of buffer - * space. - * - * "ps_drop" is not supported. - * - * "ps_ifdrop" is supported. It will return the number - * of drops the interface reports in /proc/net/dev, - * if that is available. - * - * "ps_recv" doesn't include packets not yet read from - * the kernel by libpcap. - * - * We maintain the count of packets processed by libpcap in - * "handlep->packets_read", for reasons described in the comment - * at the end of pcap_read_packet(). We have no idea how many - * packets were dropped by the kernel buffers -- but we know - * how many the interface dropped, so we can return that. - */ - stats->ps_recv = handlep->packets_read; - stats->ps_drop = 0; - stats->ps_ifdrop = handlep->stat.ps_ifdrop; - return 0; -} - -static int -add_linux_if(pcap_if_list_t *devlistp, const char *ifname, int fd, char *errbuf) -{ - const char *p; - char name[512]; /* XXX - pick a size */ - char *q, *saveq; - struct ifreq ifrflags; - - /* - * Get the interface name. - */ - p = ifname; - q = &name[0]; - while (*p != '\0' && isascii(*p) && !isspace(*p)) { - if (*p == ':') { - /* - * This could be the separator between a - * name and an alias number, or it could be - * the separator between a name with no - * alias number and the next field. - * - * If there's a colon after digits, it - * separates the name and the alias number, - * otherwise it separates the name and the - * next field. - */ - saveq = q; - while (isascii(*p) && isdigit(*p)) - *q++ = *p++; - if (*p != ':') { - /* - * That was the next field, - * not the alias number. - */ - q = saveq; - } - break; - } else - *q++ = *p++; - } - *q = '\0'; - - /* - * Get the flags for this interface. - */ - 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 */ - pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, - errno, "SIOCGIFFLAGS: %.*s", - (int)sizeof(ifrflags.ifr_name), - ifrflags.ifr_name); - return (-1); - } - - /* - * Add an entry for this interface, with no addresses, if it's - * not already in the list. - */ - if (find_or_add_if(devlistp, name, ifrflags.ifr_flags, - get_if_flags, errbuf) == NULL) { - /* - * Failure. - */ - return (-1); - } - - return (0); -} - -/* - * Get from "/sys/class/net" all interfaces listed there; if they're - * already in the list of interfaces we have, that won't add another - * instance, but if they're not, that'll add them. - * - * We don't bother getting any addresses for them; it appears you can't - * use SIOCGIFADDR on Linux to get IPv6 addresses for interfaces, and, - * although some other types of addresses can be fetched with SIOCGIFADDR, - * we don't bother with them for now. - * - * We also don't fail if we couldn't open "/sys/class/net"; we just leave - * the list of interfaces as is, and return 0, so that we can try - * scanning /proc/net/dev. - * - * Otherwise, we return 1 if we don't get an error and -1 if we do. - */ -static int -scan_sys_class_net(pcap_if_list_t *devlistp, char *errbuf) -{ - DIR *sys_class_net_d; - int fd; - struct dirent *ent; - char subsystem_path[PATH_MAX+1]; - struct stat statb; - int ret = 1; - - sys_class_net_d = opendir("/sys/class/net"); - if (sys_class_net_d == NULL) { - /* - * Don't fail if it doesn't exist at all. - */ - if (errno == ENOENT) - return (0); - - /* - * Fail if we got some other error. - */ - pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, - errno, "Can't open /sys/class/net"); - return (-1); - } - - /* - * Create a socket from which to fetch interface information. - */ - fd = socket(PF_UNIX, SOCK_RAW, 0); - if (fd < 0) { - pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, - errno, "socket"); - (void)closedir(sys_class_net_d); - return (-1); - } - - for (;;) { - errno = 0; - ent = readdir(sys_class_net_d); - if (ent == NULL) { - /* - * Error or EOF; if errno != 0, it's an error. - */ - break; - } - - /* - * Ignore "." and "..". - */ - if (strcmp(ent->d_name, ".") == 0 || - strcmp(ent->d_name, "..") == 0) - continue; - - /* - * Ignore plain files; they do not have subdirectories - * and thus have no attributes. - */ - if (ent->d_type == DT_REG) - continue; - - /* - * Is there an "ifindex" file under that name? - * (We don't care whether it's a directory or - * a symlink; older kernels have directories - * for devices, newer kernels have symlinks to - * directories.) - */ - pcap_snprintf(subsystem_path, sizeof subsystem_path, - "/sys/class/net/%s/ifindex", ent->d_name); - if (lstat(subsystem_path, &statb) != 0) { - /* - * Stat failed. Either there was an error - * other than ENOENT, and we don't know if - * this is an interface, or it's ENOENT, - * and either some part of "/sys/class/net/{if}" - * disappeared, in which case it probably means - * the interface disappeared, or there's no - * "ifindex" file, which means it's not a - * network interface. - */ - continue; - } - - /* - * Attempt to add the interface. - */ - if (add_linux_if(devlistp, &ent->d_name[0], fd, errbuf) == -1) { - /* Fail. */ - ret = -1; - break; - } - } - if (ret != -1) { - /* - * Well, we didn't fail for any other reason; did we - * fail due to an error reading the directory? - */ - if (errno != 0) { - pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, - errno, "Error reading /sys/class/net"); - ret = -1; - } - } - - (void)close(fd); - (void)closedir(sys_class_net_d); - return (ret); -} - -/* - * Get from "/proc/net/dev" all interfaces listed there; if they're - * already in the list of interfaces we have, that won't add another - * instance, but if they're not, that'll add them. - * - * See comments from scan_sys_class_net(). - */ -static int -scan_proc_net_dev(pcap_if_list_t *devlistp, char *errbuf) -{ - FILE *proc_net_f; - int fd; - char linebuf[512]; - int linenum; - char *p; - int ret = 0; - - proc_net_f = fopen("/proc/net/dev", "r"); - if (proc_net_f == NULL) { - /* - * Don't fail if it doesn't exist at all. - */ - if (errno == ENOENT) - return (0); - - /* - * Fail if we got some other error. - */ - pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, - errno, "Can't open /proc/net/dev"); - return (-1); - } - - /* - * Create a socket from which to fetch interface information. - */ - fd = socket(PF_UNIX, SOCK_RAW, 0); - if (fd < 0) { - pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, - errno, "socket"); - (void)fclose(proc_net_f); - return (-1); - } - - for (linenum = 1; - fgets(linebuf, sizeof linebuf, proc_net_f) != NULL; linenum++) { - /* - * Skip the first two lines - they're headers. - */ - if (linenum <= 2) - continue; - - p = &linebuf[0]; - - /* - * Skip leading white space. - */ - while (*p != '\0' && isascii(*p) && isspace(*p)) - p++; - if (*p == '\0' || *p == '\n') - continue; /* blank line */ - - /* - * Attempt to add the interface. - */ - if (add_linux_if(devlistp, p, fd, errbuf) == -1) { - /* Fail. */ - ret = -1; - break; - } - } - if (ret != -1) { - /* - * Well, we didn't fail for any other reason; did we - * fail due to an error reading the file? - */ - if (ferror(proc_net_f)) { - pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, - errno, "Error reading /proc/net/dev"); - ret = -1; - } - } - - (void)close(fd); - (void)fclose(proc_net_f); - return (ret); + pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, + "failed to get statistics from socket"); + return -1; } /* @@ -2659,7 +1436,7 @@ scan_proc_net_dev(pcap_if_list_t *devlistp, char *errbuf) static const char any_descr[] = "Pseudo-device that captures on all interfaces"; /* - * A SOCK_PACKET or PF_PACKET socket can be bound to any network interface. + * A PF_PACKET socket can be bound to any network interface. */ static int can_be_bound(const char *name _U_) @@ -2667,6 +1444,126 @@ can_be_bound(const char *name _U_) return (1); } +/* + * Get a socket to use with various interface ioctls. + */ +static int +get_if_ioctl_socket(void) +{ + int fd; + + /* + * This is a bit ugly. + * + * There isn't a socket type that's guaranteed to work. + * + * AF_NETLINK will work *if* you have Netlink configured into the + * kernel (can it be configured out if you have any networking + * support at all?) *and* if you're running a sufficiently recent + * kernel, but not all the kernels we support are sufficiently + * recent - that feature was introduced in Linux 4.6. + * + * AF_UNIX will work *if* you have UNIX-domain sockets configured + * into the kernel and *if* you're not on a system that doesn't + * allow them - some SELinux systems don't allow you create them. + * Most systems probably have them configured in, but not all systems + * have them configured in and allow them to be created. + * + * AF_INET will work *if* you have IPv4 configured into the kernel, + * but, apparently, some systems have network adapters but have + * kernels without IPv4 support. + * + * AF_INET6 will work *if* you have IPv6 configured into the + * kernel, but if you don't have AF_INET, you might not have + * AF_INET6, either (that is, independently on its own grounds). + * + * AF_PACKET would work, except that some of these calls should + * work even if you *don't* have capture permission (you should be + * able to enumerate interfaces and get information about them + * without capture permission; you shouldn't get a failure until + * you try pcap_activate()). (If you don't allow programs to + * get as much information as possible about interfaces if you + * don't have permission to capture, you run the risk of users + * asking "why isn't it showing XXX" - or, worse, if you don't + * show interfaces *at all* if you don't have permission to + * 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 questions.) + * + * So: + * + * we first try an AF_NETLINK socket, where "try" includes + * "try to do a device ioctl on it", as, in the future, once + * pre-4.6 kernels are sufficiently rare, that will probably + * be the mechanism most likely to work; + * + * if that fails, we try an AF_UNIX socket, as that's less + * likely to be configured out on a networking-capable system + * than is IP; + * + * if that fails, we try an AF_INET6 socket; + * + * if that fails, we try an AF_INET socket. + */ + fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + if (fd != -1) { + /* + * OK, let's make sure we can do an SIOCGIFNAME + * ioctl. + */ + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + if (ioctl(fd, SIOCGIFNAME, &ifr) == 0 || + errno != EOPNOTSUPP) { + /* + * It succeeded, or failed for some reason + * other than "netlink sockets don't support + * device ioctls". Go with the AF_NETLINK + * socket. + */ + return (fd); + } + + /* + * OK, that didn't work, so it's as bad as "netlink + * sockets aren't available". Close the socket and + * drive on. + */ + close(fd); + } + + /* + * Now try an AF_UNIX socket. + */ + fd = socket(AF_UNIX, SOCK_RAW, 0); + if (fd != -1) { + /* + * OK, we got it! + */ + return (fd); + } + + /* + * Now try an AF_INET6 socket. + */ + fd = socket(AF_INET6, SOCK_DGRAM, 0); + if (fd != -1) { + return (fd); + } + + /* + * Now try an AF_INET socket. + * + * XXX - if that fails, is there anything else we should try? + * AF_CAN, for embedded systems in vehicles, in case they're + * built without Internet protocol support? Any other socket + * types popular in non-Internet embedded systems? + */ + return (socket(AF_INET, SOCK_DGRAM, 0)); +} + /* * Get additional flags for a device, using SIOCGIFMEDIA. */ @@ -2688,7 +1585,7 @@ get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf) return 0; } - sock = socket(AF_INET, SOCK_DGRAM, 0); + sock = get_if_ioctl_socket(); if (sock == -1) { pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "Can't create socket to get ethtool information for %s", @@ -2700,14 +1597,14 @@ get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf) * OK, what type of network is this? * In particular, is it wired or wireless? */ - if (is_wifi(sock, name)) { + if (is_wifi(name)) { /* * Wi-Fi, hence wireless. */ *flags |= PCAP_IF_WIRELESS; } else { /* - * OK, what does /sys/class/net/{if}/type contain? + * OK, what does /sys/class/net/{if_name}/type contain? * (We don't use that for Wi-Fi, as it'll report * "Ethernet", i.e. ARPHRD_ETHER, for non-monitor- * mode devices.) @@ -2715,7 +1612,7 @@ get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf) char *pathstr; if (asprintf(&pathstr, "/sys/class/net/%s/type", name) == -1) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: Can't generate path name string for /sys/class/net device", name); close(sock); @@ -2729,7 +1626,6 @@ get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf) */ switch (arptype) { -#ifdef ARPHRD_LOOPBACK case ARPHRD_LOOPBACK: /* * These are types to which @@ -2743,7 +1639,6 @@ get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf) fclose(fh); free(pathstr); return 0; -#endif case ARPHRD_IRDA: case ARPHRD_IEEE80211: @@ -2766,14 +1661,25 @@ get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf) } } fclose(fh); - free(pathstr); } + free(pathstr); } #ifdef ETHTOOL_GLINK memset(&ifr, 0, sizeof(ifr)); pcap_strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); info.cmd = ETHTOOL_GLINK; + /* + * XXX - while Valgrind handles SIOCETHTOOL and knows that + * the ETHTOOL_GLINK command sets the .data member of the + * structure, Memory Sanitizer doesn't yet do so: + * + * https://bugs.llvm.org/show_bug.cgi?id=45814 + * + * For now, we zero it out to squelch warnings; if the bug + * in question is fixed, we can remove this. + */ + info.data = 0; ifr.ifr_data = (caddr_t)&info; if (ioctl(sock, SIOCETHTOOL, &ifr) == -1) { int save_errno = errno; @@ -2841,8 +1747,6 @@ get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf) int pcap_platform_finddevs(pcap_if_list_t *devlistp, char *errbuf) { - int ret; - /* * Get the list of regular interfaces first. */ @@ -2850,25 +1754,6 @@ pcap_platform_finddevs(pcap_if_list_t *devlistp, char *errbuf) get_if_flags) == -1) return (-1); /* failure */ - /* - * Read "/sys/class/net", and add to the list of interfaces all - * interfaces listed there that we don't already have, because, - * on Linux, SIOCGIFCONF reports only interfaces with IPv4 addresses, - * and even getifaddrs() won't return information about - * interfaces with no addresses, so you need to read "/sys/class/net" - * to get the names of the rest of the interfaces. - */ - ret = scan_sys_class_net(devlistp, errbuf); - if (ret == -1) - return (-1); /* failed */ - if (ret == 0) { - /* - * No /sys/class/net; try reading /proc/net/dev instead. - */ - if (scan_proc_net_dev(devlistp, errbuf) == -1) - return (-1); - } - /* * Add the "any" device. * As it refers to all network devices, not to any particular @@ -2883,188 +1768,6 @@ pcap_platform_finddevs(pcap_if_list_t *devlistp, char *errbuf) return (0); } -/* - * Attach the given BPF code to the packet capture device. - */ -static int -pcap_setfilter_linux_common(pcap_t *handle, struct bpf_program *filter, - int is_mmapped) -{ - struct pcap_linux *handlep; -#ifdef SO_ATTACH_FILTER - struct sock_fprog fcode; - int can_filter_in_kernel; - int err = 0; -#endif - - if (!handle) - return -1; - if (!filter) { - pcap_strlcpy(handle->errbuf, "setfilter: No filter specified", - PCAP_ERRBUF_SIZE); - return -1; - } - - handlep = handle->priv; - - /* Make our private copy of the filter */ - - 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 - * installing a kernel filter succeeds. - */ - handlep->filter_in_userland = 1; - - /* Install kernel level filter if possible */ - -#ifdef SO_ATTACH_FILTER -#ifdef USHRT_MAX - if (handle->fcode.bf_len > USHRT_MAX) { - /* - * fcode.len is an unsigned short for current kernel. - * I have yet to see BPF-Code with that much - * instructions but still it is possible. So for the - * sake of correctness I added this check. - */ - fprintf(stderr, "Warning: Filter too complex for kernel\n"); - fcode.len = 0; - fcode.filter = NULL; - can_filter_in_kernel = 0; - } else -#endif /* USHRT_MAX */ - { - /* - * Oh joy, the Linux kernel uses struct sock_fprog instead - * of struct bpf_program and of course the length field is - * of different size. Pointed out by Sebastian - * - * Oh, and we also need to fix it up so that all "ret" - * instructions with non-zero operands have MAXIMUM_SNAPLEN - * as the operand if we're not capturing in memory-mapped - * mode, and so that, if we're in cooked mode, all memory- - * reference instructions use special magic offsets in - * references to the link-layer header and assume that the - * link-layer payload begins at 0; "fix_program()" will do - * that. - */ - switch (fix_program(handle, &fcode, is_mmapped)) { - - case -1: - default: - /* - * Fatal error; just quit. - * (The "default" case shouldn't happen; we - * return -1 for that reason.) - */ - return -1; - - case 0: - /* - * The program performed checks that we can't make - * work in the kernel. - */ - can_filter_in_kernel = 0; - break; - - case 1: - /* - * We have a filter that'll work in the kernel. - */ - can_filter_in_kernel = 1; - break; - } - } - - /* - * NOTE: at this point, we've set both the "len" and "filter" - * fields of "fcode". As of the 2.6.32.4 kernel, at least, - * those are the only members of the "sock_fprog" structure, - * so we initialize every member of that structure. - * - * If there is anything in "fcode" that is not initialized, - * it is either a field added in a later kernel, or it's - * padding. - * - * If a new field is added, this code needs to be updated - * to set it correctly. - * - * If there are no other fields, then: - * - * if the Linux kernel looks at the padding, it's - * buggy; - * - * if the Linux kernel doesn't look at the padding, - * then if some tool complains that we're passing - * uninitialized data to the kernel, then the tool - * is buggy and needs to understand that it's just - * padding. - */ - if (can_filter_in_kernel) { - if ((err = set_kernel_filter(handle, &fcode)) == 0) - { - /* - * Installation succeded - using kernel filter, - * so userland filtering not needed. - */ - handlep->filter_in_userland = 0; - } - else if (err == -1) /* Non-fatal error */ - { - /* - * Print a warning if we weren't able to install - * the filter for a reason other than "this kernel - * isn't configured to support socket filters. - */ - if (errno != ENOPROTOOPT && errno != EOPNOTSUPP) { - fprintf(stderr, - "Warning: Kernel filter failed: %s\n", - pcap_strerror(errno)); - } - } - } - - /* - * If we're not using the kernel filter, get rid of any kernel - * filter that might've been there before, e.g. because the - * previous filter could work in the kernel, or because some other - * code attached a filter to the socket by some means other than - * calling "pcap_setfilter()". Otherwise, the kernel filter may - * filter out packets that would pass the new userland filter. - */ - if (handlep->filter_in_userland) { - if (reset_kernel_filter(handle) == -1) { - pcap_fmt_errmsg_for_errno(handle->errbuf, - PCAP_ERRBUF_SIZE, errno, - "can't remove kernel filter"); - err = -2; /* fatal error */ - } - } - - /* - * Free up the copy of the filter that was made by "fix_program()". - */ - if (fcode.filter != NULL) - free(fcode.filter); - - if (err == -2) - /* Fatal error */ - return -1; -#endif /* SO_ATTACH_FILTER */ - - return 0; -} - -static int -pcap_setfilter_linux(pcap_t *handle, struct bpf_program *filter) -{ - return pcap_setfilter_linux_common(handle, filter, 0); -} - - /* * Set direction flag: Which packets do we accept on a forwarding * single device? IN, OUT or both? @@ -3072,35 +1775,19 @@ pcap_setfilter_linux(pcap_t *handle, struct bpf_program *filter) static int pcap_setdirection_linux(pcap_t *handle, pcap_direction_t d) { -#ifdef HAVE_PF_PACKET_SOCKETS - struct pcap_linux *handlep = handle->priv; - - if (!handlep->sock_packet) { - handle->direction = d; - return 0; - } -#endif /* - * We're not using PF_PACKET sockets, so we can't determine - * the direction of the packet. + * It's guaranteed, at this point, that d is a valid + * direction value. */ - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, - "Setting direction is not supported on SOCK_PACKET sockets"); - return -1; + handle->direction = d; + return 0; } static int -is_wifi(int sock_fd -#ifndef IW_MODE_MONITOR -_U_ -#endif -, const char *device) +is_wifi(const char *device) { char *pathstr; struct stat statb; -#ifdef IW_MODE_MONITOR - char errbuf[PCAP_ERRBUF_SIZE]; -#endif /* * See if there's a sysfs wireless directory for it. @@ -3118,19 +1805,6 @@ _U_ } free(pathstr); -#ifdef IW_MODE_MONITOR - /* - * OK, maybe it's not wireless, or maybe this kernel doesn't - * support sysfs. Try the wireless extensions. - */ - if (has_wext(sock_fd, device, errbuf) == 1) { - /* - * It supports the wireless extensions, so it's a Wi-Fi - * device. - */ - return 1; - } -#endif return 0; } @@ -3152,7 +1826,7 @@ _U_ * * Sets the link type to -1 if unable to map the type. */ -static void map_arphrd_to_dlt(pcap_t *handle, int sock_fd, int arptype, +static void map_arphrd_to_dlt(pcap_t *handle, int arptype, const char *device, int cooked_ok) { static const char cdma_rmnet[] = "cdma_rmnet"; @@ -3190,12 +1864,33 @@ static void map_arphrd_to_dlt(pcap_t *handle, int sock_fd, int arptype, * 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_new()", + * 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? */ - if (!is_wifi(sock_fd, device)) { + if (!is_wifi(device)) { + int ret; + + /* + * This is not a Wi-Fi device but it could be + * a DSA master/management network device. + */ + ret = iface_dsa_get_proto_info(device, handle); + if (ret < 0) + return; + + if (ret == 1) { + /* + * This is a DSA master/management network + * device linktype is already set by + * iface_dsa_get_proto_info() set an + * appropriate offset here. + */ + handle->offset = 2; + break; + } + /* * It's not a Wi-Fi device; offer DOCSIS. */ @@ -3236,14 +1931,7 @@ static void map_arphrd_to_dlt(pcap_t *handle, int sock_fd, int arptype, #define ARPHRD_CAN 280 #endif case ARPHRD_CAN: - /* - * Map this to DLT_LINUX_SLL; that way, CAN frames will - * have ETH_P_CAN/LINUX_SLL_P_CAN as the protocol and - * CAN FD frames will have ETH_P_CANFD/LINUX_SLL_P_CANFD - * as the protocol, so they can be distinguished by the - * protocol in the SLL header. - */ - handle->linktype = DLT_LINUX_SLL; + handle->linktype = DLT_CAN_SOCKETCAN; break; #ifndef ARPHRD_IEEE802_TR @@ -3461,7 +2149,7 @@ static void map_arphrd_to_dlt(pcap_t *handle, int sock_fd, int arptype, * Back in 2002, Donald Lee at Cray wanted a DLT_ for * IP-over-FC: * - * http://www.mail-archive.com/tcpdump-workers@sandelman.ottawa.on.ca/msg01043.html + * https://www.mail-archive.com/tcpdump-workers@sandelman.ottawa.on.ca/msg01043.html * * and one was assigned. * @@ -3525,7 +2213,7 @@ static void map_arphrd_to_dlt(pcap_t *handle, int sock_fd, int arptype, /* We need to save packet direction for IrDA decoding, * so let's use "Linux-cooked" mode. Jean II * - * XXX - this is handled in activate_new(). */ + * XXX - this is handled in setup_socket(). */ /* handlep->cooked = 1; */ break; @@ -3567,7 +2255,7 @@ static void map_arphrd_to_dlt(pcap_t *handle, int sock_fd, int arptype, * pick up the netlink protocol type such as NETLINK_ROUTE, * NETLINK_GENERIC, NETLINK_FIB_LOOKUP, etc. * - * XXX - this is handled in activate_new(). + * XXX - this is handled in setup_socket(). */ /* handlep->cooked = 1; */ break; @@ -3585,66 +2273,36 @@ 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) +set_dlt_list_cooked(pcap_t *handle) { - socklen_t len; - unsigned int tp_reserve; + /* + * Support both DLT_LINUX_SLL and DLT_LINUX_SLL2. + */ + handle->dlt_list = (u_int *) malloc(sizeof(u_int) * 2); /* - * 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. + * If that failed, just leave the list empty. */ - 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; - } + 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 - * present (so the old SOCK_PACKET interface should be tried), and a - * PCAP_ERROR_ value on an error that means that the old mechanism won't - * work either (so it shouldn't be tried). + * Try to set up a PF_PACKET socket. + * Returns 0 on success and a PCAP_ERROR_ value on failure. */ static int -activate_new(pcap_t *handle) +setup_socket(pcap_t *handle, int is_any_device) { -#ifdef HAVE_PF_PACKET_SOCKETS struct pcap_linux *handlep = handle->priv; const char *device = handle->opt.device; - int is_any_device = (strcmp(device, "any") == 0); - int protocol = pcap_protocol(handle); - int sock_fd = -1, arptype, ret; -#ifdef HAVE_PACKET_AUXDATA + int status = 0; + int sock_fd, arptype; int val; -#endif int err = 0; struct packet_mreq mr; #if defined(SO_BPF_EXTENSIONS) && defined(SKF_AD_VLAN_TAG_PRESENT) @@ -3653,43 +2311,39 @@ activate_new(pcap_t *handle) #endif /* - * Open a socket with protocol family packet. If the - * "any" device was specified, we open a SOCK_DGRAM - * socket for the cooked interface, otherwise we first - * try a SOCK_RAW socket for the raw interface. + * Open a socket with protocol family packet. If cooked is true, + * we open a SOCK_DGRAM socket for the cooked interface, otherwise + * we open a SOCK_RAW socket for the raw interface. + * + * The protocol is set to 0. This means we will receive no + * packets until we "bind" the socket with a non-zero + * protocol. This allows us to setup the ring buffers without + * dropping any packets. */ sock_fd = is_any_device ? - socket(PF_PACKET, SOCK_DGRAM, protocol) : - socket(PF_PACKET, SOCK_RAW, protocol); + socket(PF_PACKET, SOCK_DGRAM, 0) : + socket(PF_PACKET, SOCK_RAW, 0); if (sock_fd == -1) { - if (errno == EINVAL || errno == EAFNOSUPPORT) { - /* - * We don't support PF_PACKET/SOCK_whatever - * sockets; try the old mechanism. - */ - return 0; - } if (errno == EPERM || errno == EACCES) { /* * You don't have permission to open the * socket. */ - ret = PCAP_ERROR_PERM_DENIED; + 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. */ - ret = PCAP_ERROR; + status = PCAP_ERROR; } pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, "socket"); - return ret; + return status; } - /* It seems the kernel supports the new interface. */ - handlep->sock_packet = 0; - /* * Get the interface index of the loopback device. * If the attempt fails, don't fail, just set the @@ -3754,7 +2408,7 @@ activate_new(pcap_t *handle) close(sock_fd); return arptype; } - map_arphrd_to_dlt(handle, sock_fd, arptype, device, 1); + map_arphrd_to_dlt(handle, arptype, device, 1); if (handle->linktype == -1 || handle->linktype == DLT_LINUX_SLL || handle->linktype == DLT_LINUX_IRDA || @@ -3771,29 +2425,29 @@ activate_new(pcap_t *handle) * type we can only determine by using * APIs that may be different on different * kernels) - reopen in cooked mode. + * + * If the type is unknown, return a warning; + * map_arphrd_to_dlt() has already set the + * warning message. */ if (close(sock_fd) == -1) { pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, "close"); return PCAP_ERROR; } - sock_fd = socket(PF_PACKET, SOCK_DGRAM, protocol); - if (sock_fd == -1) { - if (errno == EPERM || errno == EACCES) { - /* - * You don't have permission to - * open the socket. - */ - ret = PCAP_ERROR_PERM_DENIED; - } else { - /* - * Other error. - */ - ret = PCAP_ERROR; - } + sock_fd = socket(PF_PACKET, SOCK_DGRAM, 0); + if (sock_fd < 0) { + /* + * Fatal error. We treat this as + * a generic error; we already know + * that we were able to open a + * PF_PACKET/SOCK_RAW socket, so + * any failure is a "this shouldn't + * happen" case. + */ pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, "socket"); - return ret; + return PCAP_ERROR; } handlep->cooked = 1; @@ -3806,7 +2460,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); + set_dlt_list_cooked(handle); } if (handle->linktype == -1) { @@ -3816,7 +2470,7 @@ activate_new(pcap_t *handle) * update "map_arphrd_to_dlt()" * to handle the new type. */ - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "arptype %d not " "supported by libpcap - " "falling back to cooked " @@ -3833,6 +2487,12 @@ activate_new(pcap_t *handle) 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, @@ -3843,12 +2503,9 @@ activate_new(pcap_t *handle) } if ((err = iface_bind(sock_fd, handlep->ifindex, - handle->errbuf, protocol)) != 1) { - close(sock_fd); - if (err < 0) - return err; - else - return 0; /* try old mechanism */ + handle->errbuf, 0)) != 0) { + close(sock_fd); + return err; } } else { /* @@ -3869,7 +2526,7 @@ activate_new(pcap_t *handle) handle->linktype = DLT_LINUX_SLL; handle->dlt_list = NULL; handle->dlt_count = 0; - set_dlt_list_cooked(handle, sock_fd); + set_dlt_list_cooked(handle); /* * We're not bound to a device. @@ -3916,9 +2573,15 @@ activate_new(pcap_t *handle) } } - /* Enable auxillary 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) { @@ -3928,17 +2591,8 @@ activate_new(pcap_t *handle) return PCAP_ERROR; } handle->offset += VLAN_TAG_LEN; -#endif /* HAVE_PACKET_AUXDATA */ /* - * This is a 2.2[.x] or later kernel (we know that - * because we're not using a SOCK_PACKET socket - - * PF_PACKET is supported only in 2.2 and later - * kernels). - * - * We can safely pass "recvfrom()" a byte count - * based on the snapshot length. - * * If we're in cooked mode, make the snapshot length * large enough to hold a "cooked mode" header plus * 1 byte of packet data (so we don't pass a byte @@ -3956,42 +2610,18 @@ activate_new(pcap_t *handle) /* * Set the offset at which to insert VLAN tags. - * That should be the offset of the type field. */ - switch (handle->linktype) { + set_vlan_offset(handle); - 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; - } - -#if defined(SIOCGSTAMPNS) && defined(SO_TIMESTAMPNS) if (handle->opt.tstamp_precision == PCAP_TSTAMP_PRECISION_NANO) { int nsec_tstamps = 1; if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPNS, &nsec_tstamps, sizeof(nsec_tstamps)) < 0) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "setsockopt: unable to set SO_TIMESTAMPNS"); + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "setsockopt: unable to set SO_TIMESTAMPNS"); close(sock_fd); return PCAP_ERROR; } } -#endif /* defined(SIOCGSTAMPNS) && defined(SO_TIMESTAMPNS) */ /* * We've succeeded. Save the socket FD in the pcap structure. @@ -4015,30 +2645,20 @@ activate_new(pcap_t *handle) } #endif /* defined(SO_BPF_EXTENSIONS) && defined(SKF_AD_VLAN_TAG_PRESENT) */ - return 1; -#else /* HAVE_PF_PACKET_SOCKETS */ - pcap_strlcpy(ebuf, - "New packet capturing interface not supported by build " - "environment", PCAP_ERRBUF_SIZE); - return 0; -#endif /* HAVE_PF_PACKET_SOCKETS */ + return status; } -#ifdef HAVE_PACKET_RING /* - * Attempt to activate with memory-mapped access. + * 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 failure due to lack of support for memory-mapped capture, returns - * 0. - * * On error, returns -1, and sets *status to the appropriate error code; * if that is PCAP_ERROR, sets handle->errbuf to the appropriate message. */ static int -activate_mmap(pcap_t *handle, int *status) +setup_mmapped(pcap_t *handle, int *status) { struct pcap_linux *handlep = handle->priv; int ret; @@ -4062,24 +2682,18 @@ activate_mmap(pcap_t *handle, int *status) ret = prepare_tpacket_socket(handle); if (ret == -1) { free(handlep->oneshot_buffer); + handlep->oneshot_buffer = NULL; *status = PCAP_ERROR; return ret; } ret = create_ring(handle, status); - if (ret == 0) { - /* - * We don't support memory-mapped capture; our caller - * will fall back on reading from the socket. - */ - free(handlep->oneshot_buffer); - return 0; - } if (ret == -1) { /* * Error attempting to enable memory-mapped capture; * fail. create_ring() has set *status. */ free(handlep->oneshot_buffer); + handlep->oneshot_buffer = NULL; return -1; } @@ -4087,49 +2701,18 @@ activate_mmap(pcap_t *handle, int *status) * Success. *status has been set either to 0 if there are no * warnings or to a PCAP_WARNING_ value if there is a warning. * - * Override some defaults and inherit the other fields from - * activate_new. * handle->offset is used to get the current position into the rx ring. * handle->cc is used to store the ring size. */ - switch (handlep->tp_version) { - case TPACKET_V1: - handle->read_op = pcap_read_linux_mmap_v1; - break; - case TPACKET_V1_64: - handle->read_op = pcap_read_linux_mmap_v1_64; - break; -#ifdef HAVE_TPACKET2 - case TPACKET_V2: - handle->read_op = pcap_read_linux_mmap_v2; - break; -#endif -#ifdef HAVE_TPACKET3 - case TPACKET_V3: - handle->read_op = pcap_read_linux_mmap_v3; - break; -#endif - } - handle->cleanup_op = pcap_cleanup_linux_mmap; - handle->setfilter_op = pcap_setfilter_linux_mmap; - handle->setnonblock_op = pcap_setnonblock_mmap; - handle->getnonblock_op = pcap_getnonblock_mmap; - handle->oneshot_callback = pcap_oneshot_mmap; - handle->selectable_fd = handle->fd; + /* + * Set the timeout to use in poll() before returning. + */ + set_poll_timeout(handlep); + return 1; } -#else /* HAVE_PACKET_RING */ -static int -activate_mmap(pcap_t *handle _U_, int *status _U_) -{ - return 0; -} -#endif /* HAVE_PACKET_RING */ -#ifdef HAVE_PACKET_RING - -#if defined(HAVE_TPACKET2) || defined(HAVE_TPACKET3) /* * Attempt to set the socket to the specified version of the memory-mapped * header. @@ -4152,20 +2735,36 @@ init_tpacket(pcap_t *handle, int version, const char *version_str) * 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 == 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. + * EINVAL means this specific version of TPACKET + * is not supported. Tell the caller they can try + * with a different one; if they've run out of + * others to try, let them set the error message + * appropriately. */ - return 1; /* no */ + return 1; } - /* Failed to even find out; this is a fatal error. */ - pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, - errno, "can't get %s header len on packet socket", - version_str); + /* + * All other errors are fatal. + */ + if (errno == ENOPROTOOPT) { + /* + * PACKET_HDRLEN isn't supported, which means + * that memory-mapped capture isn't supported. + * Indicate that in the message. + */ + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "Kernel doesn't support memory-mapped capture; a 2.6.27 or later 2.x kernel is required, with CONFIG_PACKET_MMAP specified for 2.x kernels"); + } else { + /* + * Some unexpected error. + */ + pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, + errno, "can't get %s header len on packet socket", + version_str); + } return -1; } handlep->tp_hdrlen = val; @@ -4179,68 +2778,20 @@ init_tpacket(pcap_t *handle, int version, const char *version_str) } handlep->tp_version = version; - /* - * 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) { - pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, - errno, "can't set up reserve on packet socket"); - return -1; - } - return 0; } -#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 - * version 1. + * back to version 2. If version 2 isn't supported, just fail. * - * Return 1 if we succeed or if we fail because neither version 2 nor 3 is - * supported; return -1 on any other error, and set handle->errbuf. + * Return 0 if we succeed and -1 on any other error, and set handle->errbuf. */ static int prepare_tpacket_socket(pcap_t *handle) { - struct pcap_linux *handlep = handle->priv; -#if defined(HAVE_TPACKET2) || defined(HAVE_TPACKET3) int ret; -#endif #ifdef HAVE_TPACKET3 /* @@ -4260,7 +2811,7 @@ prepare_tpacket_socket(pcap_t *handle) /* * Success. */ - return 1; + return 0; } if (ret == -1) { /* @@ -4269,10 +2820,14 @@ prepare_tpacket_socket(pcap_t *handle) */ return -1; } + + /* + * This means it returned 1, which means "the kernel + * doesn't support TPACKET_V3"; try TPACKET_V2. + */ } #endif /* HAVE_TPACKET3 */ -#ifdef HAVE_TPACKET2 /* * Try setting the version to TPACKET_V2. */ @@ -4281,65 +2836,22 @@ prepare_tpacket_socket(pcap_t *handle) /* * Success. */ - return 1; + return 0; } - if (ret == -1) { + + if (ret == 1) { /* - * We failed for some reason other than "the - * kernel doesn't support TPACKET_V2". + * OK, the kernel supports memory-mapped capture, but + * not TPACKET_V2. Set the error message appropriately. */ - return -1; + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "Kernel doesn't support TPACKET_V2; a 2.6.27 or later kernel is required"); } -#endif /* HAVE_TPACKET2 */ /* - * 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. + * We failed. */ - 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. - * - * If we have a 32-bit userland and a 64-bit kernel, use an - * internally-defined TPACKET_V1_64, with which we use a 64-bit - * version of the data structures. - */ - if (sizeof(long) == 4) { - /* - * This is 32-bit code. - */ - struct utsname utsname; - - if (uname(&utsname) == -1) { - /* - * Failed. - */ - pcap_fmt_errmsg_for_errno(handle->errbuf, - PCAP_ERRBUF_SIZE, errno, "uname failed"); - 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; + return -1; } #define MAX(a,b) ((a)>(b)?(a):(b)) @@ -4350,9 +2862,6 @@ prepare_tpacket_socket(pcap_t *handle) * 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 failure due to lack of support for memory-mapped capture, returns - * 0. - * * On error, returns -1, and sets *status to the appropriate error code; * if that is PCAP_ERROR, sets handle->errbuf to the appropriate message. */ @@ -4363,9 +2872,9 @@ create_ring(pcap_t *handle, int *status) unsigned i, j, frames_per_block; #ifdef HAVE_TPACKET3 /* - * For sockets using TPACKET_V1 or TPACKET_V2, the extra - * stuff at the end of a struct tpacket_req3 will be - * ignored, so this is OK even for those sockets. + * For sockets using TPACKET_V2, the extra stuff at the end of a + * struct tpacket_req3 will be ignored, so this is OK even for + * those sockets. */ struct tpacket_req3 req; #else @@ -4380,13 +2889,46 @@ create_ring(pcap_t *handle, int *status) */ *status = 0; + /* + * Reserve space for VLAN tag reconstruction. + */ + tp_reserve = VLAN_TAG_LEN; + + /* + * 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, 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 - 16)? + */ + 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. + */ + 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; + } + switch (handlep->tp_version) { - case TPACKET_V1: - case TPACKET_V1_64: -#ifdef HAVE_TPACKET2 case TPACKET_V2: -#endif /* Note that with large snapshot length (say 256K, which is * the default for recent versions of tcpdump, Wireshark, * TShark, dumpcap or 64K, the value that "-s 0" has given for @@ -4456,62 +2998,6 @@ create_ring(pcap_t *handle, int *status) *status = PCAP_ERROR; return -1; } -#ifdef PACKET_RESERVE - len = sizeof(tp_reserve); - if (getsockopt(handle->fd, SOL_PACKET, PACKET_RESERVE, - &tp_reserve, &len) < 0) { - if (errno != ENOPROTOOPT) { - /* - * ENOPROTOOPT means "kernel doesn't support - * PACKET_RESERVE", in which case we fall back - * as best we can. - */ - pcap_fmt_errmsg_for_errno(handle->errbuf, - 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; - } - } -#else - /* - * 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 * LL_ALLOCATED_SPACE(dev) and vnet_hdr.hdr_len @@ -4554,49 +3040,6 @@ 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. * @@ -4615,14 +3058,14 @@ create_ring(pcap_t *handle, int *status) break; #endif default: - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Internal error: unknown TPACKET_ value %u", handlep->tp_version); *status = PCAP_ERROR; return -1; } - /* compute the minumum block size that will handle this frame. + /* compute the minimum block size that will handle this frame. * The block has to be page size aligned. * The max block size allowed by the kernel is arch-dependent and * it's not explicitly checked here. */ @@ -4671,6 +3114,9 @@ create_ring(pcap_t *handle, int *status) 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) { @@ -4683,6 +3129,8 @@ create_ring(pcap_t *handle, int *status) * try requesting hardware time stamps. */ *status = PCAP_ERROR_PERM_DENIED; + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "Attempt to set hardware timestamp failed - CAP_NET_ADMIN may be required"); return -1; case EOPNOTSUPP: @@ -4796,12 +3244,6 @@ retry: req.tp_frame_nr -= req.tp_frame_nr/20; goto retry; } - if (errno == ENOPROTOOPT) { - /* - * We don't have ring buffer support in this kernel. - */ - return 0; - } pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, "can't create rx ring on packet socket"); *status = PCAP_ERROR; @@ -4837,7 +3279,7 @@ retry: /* fill the header ring with proper frame ptr*/ handle->offset = 0; for (i=0; immapbuf[i*req.tp_block_size]; + u_char *base = &handlep->mmapbuf[i*req.tp_block_size]; for (j=0; joffset) { RING_GET_CURRENT_FRAME(handle) = base; base += req.tp_frame_size; @@ -4855,10 +3297,14 @@ destroy_ring(pcap_t *handle) { struct pcap_linux *handlep = handle->priv; - /* tell the kernel to destroy the ring*/ + /* + * Tell the kernel to destroy the ring. + * We don't check for setsockopt failure, as 1) we can't recover + * from an error and 2) we might not yet have set it up in the + * first place. + */ struct tpacket_req req; memset(&req, 0, sizeof(req)); - /* do not test for setsockopt failure, as we can't recover from any error */ (void)setsockopt(handle->fd, SOL_PACKET, PACKET_RX_RING, (void *) &req, sizeof(req)); @@ -4888,7 +3334,7 @@ destroy_ring(pcap_t *handle) * pcap_next() or pcap_next_ex(). */ static void -pcap_oneshot_mmap(u_char *user, const struct pcap_pkthdr *h, +pcap_oneshot_linux(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes) { struct oneshot_userdata *sp = (struct oneshot_userdata *)user; @@ -4900,22 +3346,8 @@ pcap_oneshot_mmap(u_char *user, const struct pcap_pkthdr *h, *sp->pkt = handlep->oneshot_buffer; } -static void -pcap_cleanup_linux_mmap( pcap_t *handle ) -{ - struct pcap_linux *handlep = handle->priv; - - destroy_ring(handle); - if (handlep->oneshot_buffer != NULL) { - free(handlep->oneshot_buffer); - handlep->oneshot_buffer = NULL; - } - pcap_cleanup_linux(handle); -} - - static int -pcap_getnonblock_mmap(pcap_t *handle) +pcap_getnonblock_linux(pcap_t *handle) { struct pcap_linux *handlep = handle->priv; @@ -4924,7 +3356,7 @@ pcap_getnonblock_mmap(pcap_t *handle) } static int -pcap_setnonblock_mmap(pcap_t *handle, int nonblock) +pcap_setnonblock_linux(pcap_t *handle, int nonblock) { struct pcap_linux *handlep = handle->priv; @@ -4947,7 +3379,23 @@ pcap_setnonblock_mmap(pcap_t *handle, int nonblock) */ 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; } @@ -4960,7 +3408,7 @@ pcap_setnonblock_mmap(pcap_t *handle, int nonblock) /* * Get the status field of the ring buffer frame at a specified offset. */ -static inline int +static inline u_int pcap_get_ring_frame_status(pcap_t *handle, int offset) { struct pcap_linux *handlep = handle->priv; @@ -4968,20 +3416,12 @@ pcap_get_ring_frame_status(pcap_t *handle, int offset) h.raw = RING_GET_FRAME_AT(handle, offset); switch (handlep->tp_version) { - case TPACKET_V1: - return (h.h1->tp_status); - break; - case TPACKET_V1_64: - return (h.h1_64->tp_status); - break; -#ifdef HAVE_TPACKET2 case TPACKET_V2: - return (h.h2->tp_status); + return __atomic_load_n(&h.h2->tp_status, __ATOMIC_ACQUIRE); break; -#endif #ifdef HAVE_TPACKET3 case TPACKET_V3: - return (h.h3->hdr.bh1.block_status); + return __atomic_load_n(&h.h3->hdr.bh1.block_status, __ATOMIC_ACQUIRE); break; #endif } @@ -4989,25 +3429,76 @@ pcap_get_ring_frame_status(pcap_t *handle, int offset) return 0; } -#ifndef POLLRDHUP -#define POLLRDHUP 0 -#endif - /* * Block waiting for frames to be available. */ static int pcap_wait_for_frames_mmap(pcap_t *handle) { struct pcap_linux *handlep = handle->priv; - char c; - struct pollfd pollinfo; + int timeout; + struct ifreq ifr; int ret; - - pollinfo.fd = handle->fd; - pollinfo.events = POLLIN; - - do { + struct pollfd pollinfo[2]; + int numpollinfo; + pollinfo[0].fd = handle->fd; + pollinfo[0].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 + * that we got told to break out of the loop, get a fatal error, + * or discover that the device went away. + * + * In non-blocking mode, we must still do one poll() to catch + * any pending error indications, but the poll() has a timeout + * of 0, so that it doesn't block, and we quit after that one + * poll(). + * + * If we've seen an ENETDOWN, it might be the first indication + * that the device went away, or it might just be that it was + * configured down. Unfortunately, there's no guarantee that + * the device has actually been removed as an interface, because: + * + * 1) if, as appears to be the case at least some of the time, + * the PF_PACKET socket code first gets a NETDEV_DOWN indication + * for the device and then gets a NETDEV_UNREGISTER indication + * for it, the first indication will cause a wakeup with ENETDOWN + * but won't set the packet socket's field for the interface index + * to -1, and the second indication won't cause a wakeup (because + * the first indication also caused the protocol hook to be + * unregistered) but will set the packet socket's field for the + * interface index to -1; + * + * 2) even if just a NETDEV_UNREGISTER indication is registered, + * the packet socket's field for the interface index only gets + * set to -1 after the wakeup, so there's a small but non-zero + * risk that a thread blocked waiting for the wakeup will get + * to the "fetch the socket name" code before the interface index + * gets set to -1, so it'll get the old interface index. + * + * Therefore, if we got an ENETDOWN and haven't seen a packet + * since then, we assume that we might be waiting for the interface + * to disappear, and poll with a timeout to try again in a short + * period of time. If we *do* see a packet, the interface has + * come back up again, and is *definitely* still there, so we + * 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. @@ -5015,67 +3506,281 @@ static int pcap_wait_for_frames_mmap(pcap_t *handle) * The timeout is 0 in non-blocking mode, so poll() * returns immediately. */ - ret = poll(&pollinfo, 1, handlep->poll_timeout); - if (ret < 0 && errno != EINTR) { - pcap_fmt_errmsg_for_errno(handle->errbuf, - PCAP_ERRBUF_SIZE, errno, - "can't poll on packet socket"); - return PCAP_ERROR; - } else if (ret > 0 && - (pollinfo.revents & (POLLHUP|POLLRDHUP|POLLERR|POLLNVAL))) { + timeout = handlep->poll_timeout; + + /* + * If we got an ENETDOWN and haven't gotten an indication + * that the device has gone away or that the device is up, + * we don't yet know for certain whether the device has + * gone away or not, do a poll() with a 1-millisecond timeout, + * as we have to poll indefinitely for "device went away" + * indications until we either get one or see that the + * device is up. + */ + if (handlep->netdown) { + if (timeout != 0) + timeout = 1; + } + ret = poll(pollinfo, numpollinfo, timeout); + if (ret < 0) { /* - * There's some indication other than - * "you can read on this descriptor" on - * the descriptor. + * Error. If it's not EINTR, report it. */ - if (pollinfo.revents & (POLLHUP | POLLRDHUP)) { - pcap_snprintf(handle->errbuf, - PCAP_ERRBUF_SIZE, - "Hangup on packet socket"); + if (errno != EINTR) { + pcap_fmt_errmsg_for_errno(handle->errbuf, + PCAP_ERRBUF_SIZE, errno, + "can't poll on packet socket"); return PCAP_ERROR; } - if (pollinfo.revents & POLLERR) { + + /* + * It's EINTR; if we were told to break out of + * the loop, do so. + */ + if (handle->break_loop) { + handle->break_loop = 0; + return PCAP_ERROR_BREAK; + } + } else if (ret > 0) { + /* + * OK, some descriptor is ready. + * Check the socket descriptor first. + * + * As I read the Linux man page, pollinfo[0].revents + * will either be POLLIN, POLLERR, POLLHUP, or POLLNVAL. + */ + if (pollinfo[0].revents == POLLIN) { /* - * A recv() will give us the actual error code. - * - * XXX - make the socket non-blocking? + * OK, we may have packets to + * read. */ - if (recv(handle->fd, &c, sizeof c, - MSG_PEEK) != -1) - continue; /* what, no error? */ - if (errno == ENETDOWN) { + break; + } + if (pollinfo[0].revents != 0) { + /* + * There's some indication other than + * "you can read on this descriptor" on + * the descriptor. + */ + if (pollinfo[0].revents & POLLNVAL) { + snprintf(handle->errbuf, + PCAP_ERRBUF_SIZE, + "Invalid polling request on packet socket"); + return PCAP_ERROR; + } + if (pollinfo[0].revents & (POLLHUP | POLLRDHUP)) { + snprintf(handle->errbuf, + PCAP_ERRBUF_SIZE, + "Hangup on packet socket"); + return PCAP_ERROR; + } + if (pollinfo[0].revents & POLLERR) { /* - * The device on which we're - * capturing went away. - * - * XXX - we should really return - * PCAP_ERROR_IFACE_NOT_UP, but - * pcap_dispatch() etc. aren't - * defined to return that. + * Get the error. */ - pcap_snprintf(handle->errbuf, - PCAP_ERRBUF_SIZE, - "The interface went down"); + int err; + socklen_t errlen; + + errlen = sizeof(err); + if (getsockopt(handle->fd, SOL_SOCKET, + SO_ERROR, &err, &errlen) == -1) { + /* + * The call *itself* returned + * an error; make *that* + * the error. + */ + err = errno; + } + + /* + * OK, we have the error. + */ + if (err == ENETDOWN) { + /* + * The device on which we're + * capturing went away or the + * interface was taken down. + * + * We don't know for certain + * which happened, and the + * next poll() may indicate + * that there are packets + * to be read, so just set + * a flag to get us to do + * checks later, and set + * the required select + * timeout to 1 millisecond + * so that event loops that + * check our socket descriptor + * also time out so that + * they can call us and we + * can do the checks. + */ + handlep->netdown = 1; + handle->required_select_timeout = &netdown_timeout; + } else if (err == 0) { + /* + * This shouldn't happen, so + * report a special indication + * that it did. + */ + snprintf(handle->errbuf, + PCAP_ERRBUF_SIZE, + "Error condition on packet socket: Reported error was 0"); + return PCAP_ERROR; + } else { + pcap_fmt_errmsg_for_errno(handle->errbuf, + PCAP_ERRBUF_SIZE, + err, + "Error condition on packet socket"); + return PCAP_ERROR; + } + } + } + /* + * Now check the event device. + */ + if (pollinfo[1].revents & POLLIN) { + ssize_t nread; + uint64_t value; + + /* + * This should never fail, but, just + * in case.... + */ + nread = read(handlep->poll_breakloop_fd, &value, + sizeof(value)); + if (nread == -1) { + pcap_fmt_errmsg_for_errno(handle->errbuf, + PCAP_ERRBUF_SIZE, + errno, + "Error reading from event FD"); + return PCAP_ERROR; + } + + /* + * According to the Linux read(2) man + * page, read() will transfer at most + * 2^31-1 bytes, so the return value is + * either -1 or a value between 0 + * and 2^31-1, so it's non-negative. + * + * Cast it to size_t to squelch + * warnings from the compiler; add this + * comment to squelch warnings from + * humans reading the code. :-) + * + * Don't treat an EOF as an error, but + * *do* treat a short read as an error; + * that "shouldn't happen", but.... + */ + if (nread != 0 && + (size_t)nread < sizeof(value)) { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "Short read from event FD: expected %zu, got %zd", + sizeof(value), nread); + return PCAP_ERROR; + } + + /* + * This event gets signaled by a + * pcap_breakloop() call; if we were told + * to break out of the loop, do so. + */ + if (handle->break_loop) { + handle->break_loop = 0; + return PCAP_ERROR_BREAK; + } + } + } + + /* + * Either: + * + * 1) we got neither an error from poll() nor any + * readable descriptors, in which case there + * are no packets waiting to read + * + * or + * + * 2) We got readable descriptors but the PF_PACKET + * socket wasn't one of them, in which case there + * are no packets waiting to read + * + * so, if we got an ENETDOWN, we've drained whatever + * packets were available to read at the point of the + * ENETDOWN. + * + * So, if we got an ENETDOWN and haven't gotten an indication + * that the device has gone away or that the device is up, + * we don't yet know for certain whether the device has + * gone away or not, check whether the device exists and is + * up. + */ + if (handlep->netdown) { + if (!device_still_exists(handle)) { + /* + * The device doesn't exist any more; + * report that. + * + * XXX - we should really return an + * appropriate error for that, but + * pcap_dispatch() etc. aren't documented + * as having error returns other than + * PCAP_ERROR or PCAP_ERROR_BREAK. + */ + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "The interface disappeared"); + return PCAP_ERROR; + } + + /* + * The device still exists; try to see if it's up. + */ + memset(&ifr, 0, sizeof(ifr)); + pcap_strlcpy(ifr.ifr_name, handlep->device, + sizeof(ifr.ifr_name)); + if (ioctl(handle->fd, SIOCGIFFLAGS, &ifr) == -1) { + if (errno == ENXIO || errno == ENODEV) { + /* + * OK, *now* it's gone. + * + * XXX - see above comment. + */ + snprintf(handle->errbuf, + PCAP_ERRBUF_SIZE, + "The interface disappeared"); + return PCAP_ERROR; } else { pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, - "Error condition on packet socket"); + "%s: Can't get flags", + handlep->device); + return PCAP_ERROR; } - return PCAP_ERROR; } - if (pollinfo.revents & POLLNVAL) { - pcap_snprintf(handle->errbuf, - PCAP_ERRBUF_SIZE, - "Invalid polling request on packet socket"); - return PCAP_ERROR; + if (ifr.ifr_flags & IFF_UP) { + /* + * It's up, so it definitely still exists. + * Cancel the ENETDOWN indication - we + * presumably got it due to the interface + * going down rather than the device going + * away - and revert to "no required select + * timeout. + */ + handlep->netdown = 0; + handle->required_select_timeout = NULL; } } - /* check for break loop condition on interrupted syscall*/ - if (handle->break_loop) { - handle->break_loop = 0; - return PCAP_ERROR_BREAK; - } - } while (ret < 0); + + /* + * If we're in non-blocking mode, just quit now, rather + * than spinning in a loop doing poll()s that immediately + * time out if there's no indication on any descriptor. + */ + if (handlep->poll_timeout == 0) + break; + } return 0; } @@ -5098,6 +3803,7 @@ static int pcap_handle_packet_mmap( unsigned char *bp; struct sockaddr_ll *sll; struct pcap_pkthdr pcaphdr; + pcap_can_socketcan_hdr *canhdr; unsigned int snaplen = tp_snaplen; struct utsname utsname; @@ -5107,7 +3813,7 @@ static int pcap_handle_packet_mmap( * Report some system information as a debugging aid. */ if (uname(&utsname) != -1) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + 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)", @@ -5115,7 +3821,7 @@ static int pcap_handle_packet_mmap( utsname.release, utsname.version, utsname.machine); } else { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + 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); @@ -5135,7 +3841,7 @@ static int pcap_handle_packet_mmap( bp = frame + tp_mac; /* if required build in place the sll header*/ - sll = (void *)frame + TPACKET_ALIGN(handlep->tp_hdrlen); + sll = (void *)(frame + TPACKET_ALIGN(handlep->tp_hdrlen)); if (handlep->cooked) { if (handle->linktype == DLT_LINUX_SLL2) { struct sll2_header *hdrp; @@ -5159,7 +3865,7 @@ static int pcap_handle_packet_mmap( if (bp < (u_char *)frame + TPACKET_ALIGN(handlep->tp_hdrlen) + sizeof(struct sockaddr_ll)) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "cooked-mode frame doesn't have room for sll header"); return -1; } @@ -5199,7 +3905,7 @@ static int pcap_handle_packet_mmap( if (bp < (u_char *)frame + TPACKET_ALIGN(handlep->tp_hdrlen) + sizeof(struct sockaddr_ll)) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "cooked-mode frame doesn't have room for sll header"); return -1; } @@ -5216,19 +3922,81 @@ static int pcap_handle_packet_mmap( snaplen += sizeof(struct sll_header); } + } else { + /* + * If this is a packet from a CAN device, so that + * sll->sll_hatype is ARPHRD_CAN, then, as we're + * not capturing in cooked mode, its link-layer + * type is DLT_CAN_SOCKETCAN. Fix up the header + * provided by the code below us to match what + * DLT_CAN_SOCKETCAN is expected to provide. + */ + if (sll->sll_hatype == ARPHRD_CAN) { + /* + * DLT_CAN_SOCKETCAN is specified as having the + * CAN ID and flags in network byte order, but + * capturing on a CAN device provides it in host + * byte order. Convert it to network byte order. + */ + canhdr = (pcap_can_socketcan_hdr *)bp; + canhdr->can_id = htonl(canhdr->can_id); + + /* + * In addition, set the CANFD_FDF flag if + * the protocol is LINUX_SLL_P_CANFD, as + * the protocol field itself isn't in + * the packet to indicate that it's a + * CAN FD packet. + */ + uint16_t protocol = ntohs(sll->sll_protocol); + if (protocol == LINUX_SLL_P_CANFD) { + canhdr->fd_flags |= CANFD_FDF; + + /* + * Zero out all the unknown bits in + * fd_flags and clear the reserved + * fields, so that a program reading + * this can assume that CANFD_FDF + * is set because we set it, not + * because some uninitialized crap + * was provided in the fd_flags + * field. + * + * (At least some LINKTYPE_CAN_SOCKETCAN + * files attached to Wireshark bugs + * had uninitialized junk there, so it + * does happen.) + * + * Update this if Linux adds more flag + * bits to the fd_flags field or uses + * either of the reserved fields for + * FD frames. + */ + canhdr->fd_flags &= ~(CANFD_FDF|CANFD_ESI|CANFD_BRS); + canhdr->reserved1 = 0; + canhdr->reserved2 = 0; + } else { + /* + * Clear CANFD_FDF if it's set (probably + * again meaning that this field is + * uninitialized junk). + */ + canhdr->fd_flags &= ~CANFD_FDF; + } + } } if (handlep->filter_in_userland && handle->fcode.bf_insns) { - struct bpf_aux_data aux_data; + struct pcap_bpf_aux_data aux_data; aux_data.vlan_tag_present = tp_vlan_tci_valid; aux_data.vlan_tag = tp_vlan_tci & 0x0fff; - if (bpf_filter_with_aux_data(handle->fcode.bf_insns, - bp, - tp_len, - snaplen, - &aux_data) == 0) + if (pcap_filter_with_aux_data(handle->fcode.bf_insns, + bp, + tp_len, + snaplen, + &aux_data) == 0) return 0; } @@ -5253,7 +4021,6 @@ static int pcap_handle_packet_mmap( } } -#if defined(HAVE_TPACKET2) || defined(HAVE_TPACKET3) if (tp_vlan_tci_valid && handlep->vlan_offset != -1 && tp_snaplen >= (unsigned int) handlep->vlan_offset) @@ -5281,7 +4048,6 @@ static int pcap_handle_packet_mmap( pcaphdr.caplen += VLAN_TAG_LEN; pcaphdr.len += VLAN_TAG_LEN; } -#endif /* * The only way to tell the kernel to cut off the @@ -5291,6 +4057,13 @@ static int pcap_handle_packet_mmap( * * Trim the snapshot length to be no longer than the * specified snapshot length. + * + * XXX - an alternative is to put a filter, consisting + * of a "ret " instruction, on the socket + * in the activate routine, so that the truncation is + * done in the kernel even if nobody specified a filter; + * that means that less buffer space is consumed in + * the memory-mapped buffer. */ if (pcaphdr.caplen > (bpf_u_int32)handle->snapshot) pcaphdr.caplen = handle->snapshot; @@ -5301,175 +4074,6 @@ static int pcap_handle_packet_mmap( return 1; } -static int -pcap_read_linux_mmap_v1(pcap_t *handle, int max_packets, pcap_handler callback, - u_char *user) -{ - struct pcap_linux *handlep = handle->priv; - union thdr h; - int pkts = 0; - int ret; - - /* wait for frames availability.*/ - h.raw = RING_GET_CURRENT_FRAME(handle); - if (h.h1->tp_status == TP_STATUS_KERNEL) { - /* - * The current frame is owned by the kernel; wait for - * a frame to be handed to us. - */ - ret = pcap_wait_for_frames_mmap(handle); - if (ret) { - return ret; - } - } - - /* 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)) { - /* - * Get the current ring buffer frame, and break if - * it's still owned by the kernel. - */ - h.raw = RING_GET_CURRENT_FRAME(handle); - if (h.h1->tp_status == TP_STATUS_KERNEL) - break; - - ret = pcap_handle_packet_mmap( - handle, - callback, - user, - h.raw, - h.h1->tp_len, - h.h1->tp_mac, - h.h1->tp_snaplen, - h.h1->tp_sec, - h.h1->tp_usec, - 0, - 0, - 0); - if (ret == 1) { - pkts++; - handlep->packets_read++; - } else if (ret < 0) { - return ret; - } - - /* - * Hand this block back to the kernel, and, if we're - * counting blocks that need to be filtered in userland - * after having been filtered by the kernel, count - * the one we've just processed. - */ - h.h1->tp_status = TP_STATUS_KERNEL; - if (handlep->blocks_to_filter_in_userland > 0) { - handlep->blocks_to_filter_in_userland--; - if (handlep->blocks_to_filter_in_userland == 0) { - /* - * No more blocks need to be filtered - * in userland. - */ - handlep->filter_in_userland = 0; - } - } - - /* next block */ - if (++handle->offset >= handle->cc) - handle->offset = 0; - - /* check for break loop condition*/ - if (handle->break_loop) { - handle->break_loop = 0; - return PCAP_ERROR_BREAK; - } - } - return pkts; -} - -static int -pcap_read_linux_mmap_v1_64(pcap_t *handle, int max_packets, pcap_handler callback, - u_char *user) -{ - struct pcap_linux *handlep = handle->priv; - union thdr h; - int pkts = 0; - int ret; - - /* wait for frames availability.*/ - h.raw = RING_GET_CURRENT_FRAME(handle); - if (h.h1_64->tp_status == TP_STATUS_KERNEL) { - /* - * The current frame is owned by the kernel; wait for - * a frame to be handed to us. - */ - ret = pcap_wait_for_frames_mmap(handle); - if (ret) { - return ret; - } - } - - /* 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)) { - /* - * Get the current ring buffer frame, and break if - * it's still owned by the kernel. - */ - h.raw = RING_GET_CURRENT_FRAME(handle); - if (h.h1_64->tp_status == TP_STATUS_KERNEL) - break; - - ret = pcap_handle_packet_mmap( - handle, - callback, - user, - h.raw, - h.h1_64->tp_len, - h.h1_64->tp_mac, - h.h1_64->tp_snaplen, - h.h1_64->tp_sec, - h.h1_64->tp_usec, - 0, - 0, - 0); - if (ret == 1) { - pkts++; - handlep->packets_read++; - } else if (ret < 0) { - return ret; - } - - /* - * Hand this block back to the kernel, and, if we're - * counting blocks that need to be filtered in userland - * after having been filtered by the kernel, count - * the one we've just processed. - */ - h.h1_64->tp_status = TP_STATUS_KERNEL; - if (handlep->blocks_to_filter_in_userland > 0) { - handlep->blocks_to_filter_in_userland--; - if (handlep->blocks_to_filter_in_userland == 0) { - /* - * No more blocks need to be filtered - * in userland. - */ - handlep->filter_in_userland = 0; - } - } - - /* next block */ - if (++handle->offset >= handle->cc) - handle->offset = 0; - - /* check for break loop condition*/ - if (handle->break_loop) { - handle->break_loop = 0; - return PCAP_ERROR_BREAK; - } - } - return pkts; -} - -#ifdef HAVE_TPACKET2 static int pcap_read_linux_mmap_v2(pcap_t *handle, int max_packets, pcap_handler callback, u_char *user) @@ -5481,7 +4085,7 @@ pcap_read_linux_mmap_v2(pcap_t *handle, int max_packets, pcap_handler callback, /* wait for frames availability.*/ h.raw = RING_GET_CURRENT_FRAME(handle); - if (h.h2->tp_status == TP_STATUS_KERNEL) { + if (!packet_mmap_acquire(h.h2)) { /* * The current frame is owned by the kernel; wait for * a frame to be handed to us. @@ -5492,15 +4096,28 @@ pcap_read_linux_mmap_v2(pcap_t *handle, int max_packets, pcap_handler callback, } } - /* 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. */ h.raw = RING_GET_CURRENT_FRAME(handle); - if (h.h2->tp_status == TP_STATUS_KERNEL) + if (!packet_mmap_acquire(h.h2)) break; ret = pcap_handle_packet_mmap( @@ -5518,7 +4135,6 @@ pcap_read_linux_mmap_v2(pcap_t *handle, int max_packets, pcap_handler callback, VLAN_TPID(h.h2, h.h2)); if (ret == 1) { pkts++; - handlep->packets_read++; } else if (ret < 0) { return ret; } @@ -5529,7 +4145,7 @@ pcap_read_linux_mmap_v2(pcap_t *handle, int max_packets, pcap_handler callback, * after having been filtered by the kernel, count * the one we've just processed. */ - h.h2->tp_status = TP_STATUS_KERNEL; + packet_mmap_release(h.h2); if (handlep->blocks_to_filter_in_userland > 0) { handlep->blocks_to_filter_in_userland--; if (handlep->blocks_to_filter_in_userland == 0) { @@ -5553,7 +4169,6 @@ pcap_read_linux_mmap_v2(pcap_t *handle, int max_packets, pcap_handler callback, } return pkts; } -#endif /* HAVE_TPACKET2 */ #ifdef HAVE_TPACKET3 static int @@ -5569,7 +4184,7 @@ again: if (handlep->current_packet == NULL) { /* wait for frames availability.*/ h.raw = RING_GET_CURRENT_FRAME(handle); - if (h.h3->hdr.bh1.block_status == TP_STATUS_KERNEL) { + if (!packet_mmap_v3_acquire(h.h3)) { /* * The current frame is owned by the kernel; wait * for a frame to be handed to us. @@ -5581,7 +4196,7 @@ again: } } h.raw = RING_GET_CURRENT_FRAME(handle); - if (h.h3->hdr.bh1.block_status == TP_STATUS_KERNEL) { + if (!packet_mmap_v3_acquire(h.h3)) { if (pkts == 0 && handlep->timeout == 0) { /* Block until we see a packet. */ goto again; @@ -5589,14 +4204,27 @@ again: 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) { h.raw = RING_GET_CURRENT_FRAME(handle); - if (h.h3->hdr.bh1.block_status == TP_STATUS_KERNEL) + if (!packet_mmap_v3_acquire(h.h3)) break; handlep->current_packet = h.raw + h.h3->hdr.bh1.offset_to_first_pkt; @@ -5604,12 +4232,12 @@ again: } 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; @@ -5632,7 +4260,6 @@ again: VLAN_TPID(tp3_hdr, &tp3_hdr->hv1)); if (ret == 1) { pkts++; - handlep->packets_read++; } else if (ret < 0) { handlep->current_packet = NULL; return ret; @@ -5649,7 +4276,7 @@ again: * filtered by the kernel, count the one we've * just processed. */ - h.h3->hdr.bh1.block_status = TP_STATUS_KERNEL; + packet_mmap_v3_release(h.h3); if (handlep->blocks_to_filter_in_userland > 0) { handlep->blocks_to_filter_in_userland--; if (handlep->blocks_to_filter_in_userland == 0) { @@ -5682,29 +4309,191 @@ again: } #endif /* HAVE_TPACKET3 */ +/* + * Attach the given BPF code to the packet capture device. + */ static int -pcap_setfilter_linux_mmap(pcap_t *handle, struct bpf_program *filter) +pcap_setfilter_linux(pcap_t *handle, struct bpf_program *filter) { - struct pcap_linux *handlep = handle->priv; - int n, offset; - int ret; + struct pcap_linux *handlep; + struct sock_fprog fcode; + int can_filter_in_kernel; + int err = 0; + int n, offset; + + if (!handle) + return -1; + if (!filter) { + pcap_strlcpy(handle->errbuf, "setfilter: No filter specified", + PCAP_ERRBUF_SIZE); + return -1; + } + + handlep = handle->priv; + + /* Make our private copy of the filter */ + + if (install_bpf_program(handle, filter) < 0) + /* install_bpf_program() filled in errbuf */ + return -1; /* - * Don't rewrite "ret" instructions; we don't need to, as - * we're not reading packets with recvmsg(), and we don't - * want to, as, by not rewriting them, the kernel can avoid - * copying extra data. + * Run user level packet filter by default. Will be overridden if + * installing a kernel filter succeeds. */ - ret = pcap_setfilter_linux_common(handle, filter, 1); - if (ret < 0) - return ret; + handlep->filter_in_userland = 1; + + /* Install kernel level filter if possible */ + +#ifdef USHRT_MAX + if (handle->fcode.bf_len > USHRT_MAX) { + /* + * fcode.len is an unsigned short for current kernel. + * I have yet to see BPF-Code with that much + * instructions but still it is possible. So for the + * sake of correctness I added this check. + */ + fprintf(stderr, "Warning: Filter too complex for kernel\n"); + fcode.len = 0; + fcode.filter = NULL; + can_filter_in_kernel = 0; + } else +#endif /* USHRT_MAX */ + { + /* + * Oh joy, the Linux kernel uses struct sock_fprog instead + * of struct bpf_program and of course the length field is + * of different size. Pointed out by Sebastian + * + * Oh, and we also need to fix it up so that all "ret" + * instructions with non-zero operands have MAXIMUM_SNAPLEN + * as the operand if we're not capturing in memory-mapped + * mode, and so that, if we're in cooked mode, all memory- + * reference instructions use special magic offsets in + * references to the link-layer header and assume that the + * link-layer payload begins at 0; "fix_program()" will do + * that. + */ + switch (fix_program(handle, &fcode)) { + + case -1: + default: + /* + * Fatal error; just quit. + * (The "default" case shouldn't happen; we + * return -1 for that reason.) + */ + return -1; + + case 0: + /* + * The program performed checks that we can't make + * work in the kernel. + */ + can_filter_in_kernel = 0; + break; + + case 1: + /* + * We have a filter that'll work in the kernel. + */ + can_filter_in_kernel = 1; + break; + } + } + + /* + * NOTE: at this point, we've set both the "len" and "filter" + * fields of "fcode". As of the 2.6.32.4 kernel, at least, + * those are the only members of the "sock_fprog" structure, + * so we initialize every member of that structure. + * + * If there is anything in "fcode" that is not initialized, + * it is either a field added in a later kernel, or it's + * padding. + * + * If a new field is added, this code needs to be updated + * to set it correctly. + * + * If there are no other fields, then: + * + * if the Linux kernel looks at the padding, it's + * buggy; + * + * if the Linux kernel doesn't look at the padding, + * then if some tool complains that we're passing + * uninitialized data to the kernel, then the tool + * is buggy and needs to understand that it's just + * padding. + */ + if (can_filter_in_kernel) { + if ((err = set_kernel_filter(handle, &fcode)) == 0) + { + /* + * Installation succeeded - using kernel filter, + * so userland filtering not needed. + */ + handlep->filter_in_userland = 0; + } + else if (err == -1) /* Non-fatal error */ + { + /* + * Print a warning if we weren't able to install + * the filter for a reason other than "this kernel + * isn't configured to support socket filters. + */ + 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)); + } + } + } + + /* + * If we're not using the kernel filter, get rid of any kernel + * filter that might've been there before, e.g. because the + * previous filter could work in the kernel, or because some other + * code attached a filter to the socket by some means other than + * calling "pcap_setfilter()". Otherwise, the kernel filter may + * filter out packets that would pass the new userland filter. + */ + if (handlep->filter_in_userland) { + if (reset_kernel_filter(handle) == -1) { + pcap_fmt_errmsg_for_errno(handle->errbuf, + PCAP_ERRBUF_SIZE, errno, + "can't remove kernel filter"); + err = -2; /* fatal error */ + } + } + + /* + * Free up the copy of the filter that was made by "fix_program()". + */ + if (fcode.filter != NULL) + free(fcode.filter); + + if (err == -2) + /* Fatal error */ + return -1; /* * If we're filtering in userland, there's nothing to do; * the new filter will be used for the next packet. */ if (handlep->filter_in_userland) - return ret; + return 0; /* * We're filtering in the kernel; the packets present in @@ -5755,13 +4544,10 @@ pcap_setfilter_linux_mmap(pcap_t *handle, struct bpf_program *filter) */ handlep->blocks_to_filter_in_userland = handle->cc - n; handlep->filter_in_userland = 1; - return ret; + + return 0; } -#endif /* HAVE_PACKET_RING */ - - -#ifdef HAVE_PF_PACKET_SOCKETS /* * Return the index of the given device name. Fill ebuf and return * -1 on failure. @@ -5785,19 +4571,18 @@ iface_get_id(int fd, const char *device, char *ebuf) /* * Bind the socket associated with FD to the given device. - * Return 1 on success, 0 if we should try a SOCK_PACKET socket, - * or a PCAP_ERROR_ value on a hard error. + * Return 0 on success or a PCAP_ERROR_ value on a hard error. */ static int iface_bind(int fd, int ifindex, char *ebuf, int protocol) { struct sockaddr_ll sll; - int err; + int ret, err; socklen_t errlen = sizeof(err); memset(&sll, 0, sizeof(sll)); sll.sll_family = AF_PACKET; - sll.sll_ifindex = ifindex; + sll.sll_ifindex = ifindex < 0 ? 0 : ifindex; sll.sll_protocol = protocol; if (bind(fd, (struct sockaddr *) &sll, sizeof(sll)) == -1) { @@ -5810,11 +4595,20 @@ iface_bind(int fd, int ifindex, char *ebuf, int protocol) * libpcap developers. */ return PCAP_ERROR_IFACE_NOT_UP; + } + if (errno == ENODEV) { + /* + * There's nothing more to say, so clear the + * error message. + */ + ebuf[0] = '\0'; + ret = PCAP_ERROR_NO_SUCH_DEVICE; } else { + ret = PCAP_ERROR; pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, errno, "bind"); - return PCAP_ERROR; } + return ret; } /* Any pending errors, e.g., network is down? */ @@ -5822,7 +4616,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 (SO_ERROR)"); - return 0; + return PCAP_ERROR; } if (err == ENETDOWN) { @@ -5837,391 +4631,89 @@ iface_bind(int fd, int ifindex, char *ebuf, int protocol) } else if (err > 0) { pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, err, "bind"); - return 0; - } - - return 1; -} - -#ifdef IW_MODE_MONITOR -/* - * Check whether the device supports the Wireless Extensions. - * Returns 1 if it does, 0 if it doesn't, PCAP_ERROR_NO_SUCH_DEVICE - * if the device doesn't even exist. - */ -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 */ - - 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); - return ret; -} - -/* - * Per me si va ne la citta dolente, - * Per me si va ne l'etterno dolore, - * ... - * Lasciate ogne speranza, voi ch'intrate. - * - * XXX - airmon-ng does special stuff with the Orinoco driver and the - * wlan-ng driver. - */ -typedef enum { - MONITOR_WEXT, - MONITOR_HOSTAP, - MONITOR_PRISM, - MONITOR_PRISM54, - MONITOR_ACX100, - MONITOR_RT2500, - MONITOR_RT2570, - MONITOR_RT73, - MONITOR_RTL8XXX -} monitor_type; - -/* - * Use the Wireless Extensions, if we have them, to try to turn monitor mode - * on if it's not already on. - * - * Returns 1 on success, 0 if we don't support the Wireless Extensions - * on this device, or a PCAP_ERROR_ value if we do support them but - * we weren't able to turn monitor mode on. - */ -static int -enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) -{ - /* - * XXX - at least some adapters require non-Wireless Extensions - * mechanisms to turn monitor mode on. - * - * Atheros cards might require that a separate "monitor virtual access - * point" be created, with later versions of the madwifi driver. - * airmon-ng does "wlanconfig ath create wlandev {if} wlanmode - * monitor -bssid", which apparently spits out a line "athN" - * where "athN" is the monitor mode device. To leave monitor - * mode, it destroys the monitor mode device. - * - * Some Intel Centrino adapters might require private ioctls to get - * radio headers; the ipw2200 and ipw3945 drivers allow you to - * configure a separate "rtapN" interface to capture in monitor - * mode without preventing the adapter from operating normally. - * (airmon-ng doesn't appear to use that, though.) - * - * It would be Truly Wonderful if mac80211 and nl80211 cleaned this - * up, and if all drivers were converted to mac80211 drivers. - * - * If interface {if} is a mac80211 driver, the file - * /sys/class/net/{if}/phy80211 is a symlink to - * /sys/class/ieee80211/{phydev}, for some {phydev}. - * - * On Fedora 9, with a 2.6.26.3-29 kernel, my Zydas stick, at - * least, has a "wmaster0" device and a "wlan0" device; the - * latter is the one with the IP address. Both show up in - * "tcpdump -D" output. Capturing on the wmaster0 device - * captures with 802.11 headers. - * - * airmon-ng searches through /sys/class/net for devices named - * monN, starting with mon0; as soon as one *doesn't* exist, - * it chooses that as the monitor device name. If the "iw" - * command exists, it does "iw dev {if} interface add {monif} - * type monitor", where {monif} is the monitor device. It - * then (sigh) sleeps .1 second, and then configures the - * device up. Otherwise, if /sys/class/ieee80211/{phydev}/add_iface - * is a file, it writes {mondev}, without a newline, to that file, - * and again (sigh) sleeps .1 second, and then iwconfig's that - * device into monitor mode and configures it up. Otherwise, - * you can't do monitor mode. - * - * All these devices are "glued" together by having the - * /sys/class/net/{device}/phy80211 links pointing to the same - * place, so, given a wmaster, wlan, or mon device, you can - * find the other devices by looking for devices with - * the same phy80211 link. - * - * To turn monitor mode off, delete the monitor interface, - * either with "iw dev {monif} interface del" or by sending - * {monif}, with no NL, down /sys/class/ieee80211/{phydev}/remove_iface - * - * Note: if you try to create a monitor device named "monN", and - * there's already a "monN" device, it fails, as least with - * the netlink interface (which is what iw uses), with a return - * value of -ENFILE. (Return values are negative errnos.) We - * could probably use that to find an unused device. - */ - struct pcap_linux *handlep = handle->priv; - int err; - struct iwreq ireq; - struct iw_priv_args *priv; - monitor_type montype; - int i; - __u32 cmd; - struct ifreq ifr; - int oldflags; - int args[2]; - int channel; - - /* - * Does this device *support* the Wireless Extensions? - */ - err = has_wext(sock_fd, device, handle->errbuf); - if (err <= 0) - return err; /* either it doesn't or the device doesn't even exist */ - /* - * Start out assuming we have no private extensions to control - * radio metadata. - */ - montype = MONITOR_WEXT; - cmd = 0; - - /* - * Try to get all the Wireless Extensions private ioctls - * supported by this device. - * - * First, get the size of the buffer we need, by supplying no - * buffer and a length of 0. If the device supports private - * ioctls, it should return E2BIG, with ireq.u.data.length set - * to the length we need. If it doesn't support them, it should - * return EOPNOTSUPP. - */ - memset(&ireq, 0, sizeof ireq); - 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; - ireq.u.data.flags = 0; - if (ioctl(sock_fd, SIOCGIWPRIV, &ireq) != -1) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, - "%s: SIOCGIWPRIV with a zero-length buffer didn't fail!", - device); return PCAP_ERROR; } - if (errno != EOPNOTSUPP) { + + return 0; +} + +/* + * Try to enter monitor mode. + * If we have libnl, try to create a new monitor-mode device and + * capture on that; otherwise, just say "not supported". + */ +#ifdef HAVE_LIBNL +static int +enter_rfmon_mode(pcap_t *handle, int sock_fd, const char *device) +{ + struct pcap_linux *handlep = handle->priv; + int ret; + char phydev_path[PATH_MAX+1]; + struct nl80211_state nlstate; + struct ifreq ifr; + u_int n; + + /* + * Is this a mac80211 device? + */ + ret = get_mac80211_phydev(handle, device, phydev_path, PATH_MAX); + if (ret < 0) + return ret; /* error */ + if (ret == 0) + return 0; /* no error, but not mac80211 device */ + + /* + * XXX - is this already a monN device? + * If so, we're done. + */ + + /* + * OK, it's apparently a mac80211 device. + * Try to find an unused monN device for it. + */ + ret = nl80211_init(handle, &nlstate, device); + if (ret != 0) + return ret; + for (n = 0; n < UINT_MAX; n++) { /* - * OK, it's not as if there are no private ioctls. + * Try mon{n}. */ - if (errno != E2BIG) { + char mondevice[3+10+1]; /* mon{UINT_MAX}\0 */ + + snprintf(mondevice, sizeof mondevice, "mon%u", n); + ret = add_mon_if(handle, sock_fd, &nlstate, device, mondevice); + if (ret == 1) { /* - * Failed. + * Success. We don't clean up the libnl state + * yet, as we'll be using it later. */ - pcap_fmt_errmsg_for_errno(handle->errbuf, - PCAP_ERRBUF_SIZE, errno, "%s: SIOCGIWPRIV", device); - return PCAP_ERROR; + goto added; } - - /* - * OK, try to get the list of private ioctls. - */ - priv = malloc(ireq.u.data.length * sizeof (struct iw_priv_args)); - if (priv == NULL) { - pcap_fmt_errmsg_for_errno(handle->errbuf, - PCAP_ERRBUF_SIZE, errno, "malloc"); - return PCAP_ERROR; + if (ret < 0) { + /* + * Hard failure. Just return ret; handle->errbuf + * has already been set. + */ + nl80211_cleanup(&nlstate); + return ret; } - ireq.u.data.pointer = (void *)priv; - if (ioctl(sock_fd, SIOCGIWPRIV, &ireq) == -1) { - pcap_fmt_errmsg_for_errno(handle->errbuf, - PCAP_ERRBUF_SIZE, errno, "%s: SIOCGIWPRIV", device); - free(priv); - return PCAP_ERROR; - } - - /* - * Look for private ioctls to turn monitor mode on or, if - * monitor mode is on, to set the header type. - */ - for (i = 0; i < ireq.u.data.length; i++) { - if (strcmp(priv[i].name, "monitor_type") == 0) { - /* - * Hostap driver, use this one. - * Set monitor mode first. - * You can set it to 0 to get DLT_IEEE80211, - * 1 to get DLT_PRISM, 2 to get - * DLT_IEEE80211_RADIO_AVS, and, with more - * recent versions of the driver, 3 to get - * DLT_IEEE80211_RADIO. - */ - if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT) - break; - if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED)) - break; - if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 1) - break; - montype = MONITOR_HOSTAP; - cmd = priv[i].cmd; - break; - } - if (strcmp(priv[i].name, "set_prismhdr") == 0) { - /* - * Prism54 driver, use this one. - * Set monitor mode first. - * You can set it to 2 to get DLT_IEEE80211 - * or 3 or get DLT_PRISM. - */ - if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT) - break; - if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED)) - break; - if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 1) - break; - montype = MONITOR_PRISM54; - cmd = priv[i].cmd; - break; - } - if (strcmp(priv[i].name, "forceprismheader") == 0) { - /* - * RT2570 driver, use this one. - * Do this after turning monitor mode on. - * You can set it to 1 to get DLT_PRISM or 2 - * to get DLT_IEEE80211. - */ - if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT) - break; - if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED)) - break; - if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 1) - break; - montype = MONITOR_RT2570; - cmd = priv[i].cmd; - break; - } - if (strcmp(priv[i].name, "forceprism") == 0) { - /* - * RT73 driver, use this one. - * Do this after turning monitor mode on. - * Its argument is a *string*; you can - * set it to "1" to get DLT_PRISM or "2" - * to get DLT_IEEE80211. - */ - if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_CHAR) - break; - if (priv[i].set_args & IW_PRIV_SIZE_FIXED) - break; - montype = MONITOR_RT73; - cmd = priv[i].cmd; - break; - } - if (strcmp(priv[i].name, "prismhdr") == 0) { - /* - * One of the RTL8xxx drivers, use this one. - * It can only be done after monitor mode - * has been turned on. You can set it to 1 - * to get DLT_PRISM or 0 to get DLT_IEEE80211. - */ - if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT) - break; - if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED)) - break; - if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 1) - break; - montype = MONITOR_RTL8XXX; - cmd = priv[i].cmd; - break; - } - if (strcmp(priv[i].name, "rfmontx") == 0) { - /* - * RT2500 or RT61 driver, use this one. - * It has one one-byte parameter; set - * u.data.length to 1 and u.data.pointer to - * point to the parameter. - * It doesn't itself turn monitor mode on. - * You can set it to 1 to allow transmitting - * in monitor mode(?) and get DLT_IEEE80211, - * or set it to 0 to disallow transmitting in - * monitor mode(?) and get DLT_PRISM. - */ - if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT) - break; - if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 2) - break; - montype = MONITOR_RT2500; - cmd = priv[i].cmd; - break; - } - if (strcmp(priv[i].name, "monitor") == 0) { - /* - * Either ACX100 or hostap, use this one. - * It turns monitor mode on. - * If it takes two arguments, it's ACX100; - * the first argument is 1 for DLT_PRISM - * or 2 for DLT_IEEE80211, and the second - * argument is the channel on which to - * run. If it takes one argument, it's - * HostAP, and the argument is 2 for - * DLT_IEEE80211 and 3 for DLT_PRISM. - * - * If we see this, we don't quit, as this - * might be a version of the hostap driver - * that also supports "monitor_type". - */ - if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT) - break; - if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED)) - break; - switch (priv[i].set_args & IW_PRIV_SIZE_MASK) { - - case 1: - montype = MONITOR_PRISM; - cmd = priv[i].cmd; - break; - - case 2: - montype = MONITOR_ACX100; - cmd = priv[i].cmd; - break; - - default: - break; - } - } - } - free(priv); } - /* - * XXX - ipw3945? islism? - */ + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "%s: No free monN interfaces", device); + nl80211_cleanup(&nlstate); + return PCAP_ERROR; - /* - * Get the old mode. - */ - pcap_strlcpy(ireq.ifr_ifrn.ifrn_name, device, - sizeof ireq.ifr_ifrn.ifrn_name); - if (ioctl(sock_fd, SIOCGIWMODE, &ireq) == -1) { - /* - * We probably won't be able to set the mode, either. - */ - return PCAP_ERROR_RFMON_NOTSUP; - } +added: +#if 0 /* - * Is it currently in monitor mode? - */ - if (ireq.u.mode == IW_MODE_MONITOR) { - /* - * Yes. Just leave things as they are. - * We don't offer multiple link-layer types, as - * changing the link-layer type out from under - * somebody else capturing in monitor mode would - * be considered rude. - */ - return 1; - } - /* - * No. We have to put the adapter into rfmon mode. + * Sleep for .1 seconds. */ + delay.tv_sec = 0; + delay.tv_nsec = 500000000; + nanosleep(&delay, NULL); +#endif /* * If we haven't already done so, arrange to have @@ -6232,277 +4724,47 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) * "atexit()" failed; don't put the interface * in rfmon mode, just give up. */ - return PCAP_ERROR_RFMON_NOTSUP; - } - - /* - * Save the old mode. - */ - handlep->oldmode = ireq.u.mode; - - /* - * Put the adapter in rfmon mode. How we do this depends - * on whether we have a special private ioctl or not. - */ - if (montype == MONITOR_PRISM) { - /* - * We have the "monitor" private ioctl, but none of - * the other private ioctls. Use this, and select - * the Prism header. - * - * If it fails, just fall back on SIOCSIWMODE. - */ - memset(&ireq, 0, sizeof ireq); - 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 */ - memcpy(ireq.u.name, args, sizeof (int)); - if (ioctl(sock_fd, cmd, &ireq) != -1) { - /* - * Success. - * Note that we have to put the old mode back - * when we close the device. - */ - handlep->must_do_on_close |= MUST_CLEAR_RFMON; - - /* - * Add this to the list of pcaps to close - * when we exit. - */ - pcap_add_to_pcaps_to_close(handle); - - return 1; - } - - /* - * Failure. Fall back on SIOCSIWMODE. - */ - } - - /* - * First, take the interface down if it's up; otherwise, we - * might get EBUSY. - */ - memset(&ifr, 0, sizeof(ifr)); - 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); + del_mon_if(handle, sock_fd, &nlstate, device, + handlep->mondevice); + nl80211_cleanup(&nlstate); return PCAP_ERROR; } - oldflags = 0; - if (ifr.ifr_flags & IFF_UP) { - oldflags = ifr.ifr_flags; - ifr.ifr_flags &= ~IFF_UP; - if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1) { - pcap_fmt_errmsg_for_errno(handle->errbuf, - PCAP_ERRBUF_SIZE, errno, "%s: Can't set flags", - device); - return PCAP_ERROR; - } + + /* + * Now configure the monitor interface up. + */ + memset(&ifr, 0, sizeof(ifr)); + 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, + handlep->mondevice); + del_mon_if(handle, sock_fd, &nlstate, device, + handlep->mondevice); + nl80211_cleanup(&nlstate); + return PCAP_ERROR; + } + ifr.ifr_flags |= IFF_UP|IFF_RUNNING; + if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1) { + pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, + errno, "%s: Can't set flags for %s", device, + handlep->mondevice); + del_mon_if(handle, sock_fd, &nlstate, device, + handlep->mondevice); + nl80211_cleanup(&nlstate); + return PCAP_ERROR; } /* - * Then turn monitor mode on. + * Success. Clean up the libnl state. */ - 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) { - /* - * Scientist, you've failed. - * Bring the interface back up if we shut it down. - */ - ifr.ifr_flags = oldflags; - if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1) { - pcap_fmt_errmsg_for_errno(handle->errbuf, - PCAP_ERRBUF_SIZE, errno, "%s: Can't set flags", - device); - return PCAP_ERROR; - } - return PCAP_ERROR_RFMON_NOTSUP; - } + nl80211_cleanup(&nlstate); /* - * XXX - airmon-ng does "iwconfig {if} key off" after setting - * monitor mode and setting the channel, and then does - * "iwconfig up". + * Note that we have to delete the monitor device when we close + * the handle. */ - - /* - * Now select the appropriate radio header. - */ - switch (montype) { - - case MONITOR_WEXT: - /* - * We don't have any private ioctl to set the header. - */ - break; - - case MONITOR_HOSTAP: - /* - * Try to select the radiotap header. - */ - memset(&ireq, 0, sizeof ireq); - 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)); - if (ioctl(sock_fd, cmd, &ireq) != -1) - break; /* success */ - - /* - * That failed. Try to select the AVS header. - */ - memset(&ireq, 0, sizeof ireq); - 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)); - if (ioctl(sock_fd, cmd, &ireq) != -1) - break; /* success */ - - /* - * That failed. Try to select the Prism header. - */ - memset(&ireq, 0, sizeof ireq); - 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)); - ioctl(sock_fd, cmd, &ireq); - break; - - case MONITOR_PRISM: - /* - * The private ioctl failed. - */ - break; - - case MONITOR_PRISM54: - /* - * Select the Prism header. - */ - memset(&ireq, 0, sizeof ireq); - 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)); - ioctl(sock_fd, cmd, &ireq); - break; - - case MONITOR_ACX100: - /* - * Get the current channel. - */ - memset(&ireq, 0, sizeof ireq); - 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, - PCAP_ERRBUF_SIZE, errno, "%s: SIOCGIWFREQ", device); - return PCAP_ERROR; - } - channel = ireq.u.freq.m; - - /* - * Select the Prism header, and set the channel to the - * current value. - */ - memset(&ireq, 0, sizeof ireq); - 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 */ - memcpy(ireq.u.name, args, 2*sizeof (int)); - ioctl(sock_fd, cmd, &ireq); - break; - - case MONITOR_RT2500: - /* - * Disallow transmission - that turns on the - * Prism header. - */ - memset(&ireq, 0, sizeof ireq); - 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)); - ioctl(sock_fd, cmd, &ireq); - break; - - case MONITOR_RT2570: - /* - * Force the Prism header. - */ - memset(&ireq, 0, sizeof ireq); - 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)); - ioctl(sock_fd, cmd, &ireq); - break; - - case MONITOR_RT73: - /* - * Force the Prism header. - */ - memset(&ireq, 0, sizeof ireq); - 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"; - ireq.u.data.flags = 0; - ioctl(sock_fd, cmd, &ireq); - break; - - case MONITOR_RTL8XXX: - /* - * Force the Prism header. - */ - memset(&ireq, 0, sizeof ireq); - 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)); - ioctl(sock_fd, cmd, &ireq); - break; - } - - /* - * Now bring the interface back up if we brought it down. - */ - if (oldflags != 0) { - ifr.ifr_flags = oldflags; - if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1) { - pcap_fmt_errmsg_for_errno(handle->errbuf, - PCAP_ERRBUF_SIZE, errno, "%s: Can't set flags", - device); - - /* - * At least try to restore the old mode on the - * interface. - */ - if (ioctl(handle->fd, SIOCSIWMODE, &ireq) == -1) { - /* - * Scientist, you've failed. - */ - fprintf(stderr, - "Can't restore interface wireless mode (SIOCSIWMODE failed: %s).\n" - "Please adjust manually.\n", - strerror(errno)); - } - return PCAP_ERROR; - } - } - - /* - * Note that we have to put the old mode back when we - * close the device. - */ - handlep->must_do_on_close |= MUST_CLEAR_RFMON; + handlep->must_do_on_close |= MUST_DELETE_MONIF; /* * Add this to the list of pcaps to close when we exit. @@ -6511,41 +4773,16 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) return 1; } -#endif /* IW_MODE_MONITOR */ - -/* - * Try various mechanisms to enter monitor mode. - */ +#else /* HAVE_LIBNL */ static int -enter_rfmon_mode(pcap_t *handle, int sock_fd, const char *device) +enter_rfmon_mode(pcap_t *handle _U_, int sock_fd _U_, const char *device _U_) { -#if defined(HAVE_LIBNL) || defined(IW_MODE_MONITOR) - int ret; -#endif - -#ifdef HAVE_LIBNL - ret = enter_rfmon_mode_mac80211(handle, sock_fd, device); - if (ret < 0) - return ret; /* error attempting to do so */ - if (ret == 1) - return 1; /* success */ -#endif /* HAVE_LIBNL */ - -#ifdef IW_MODE_MONITOR - ret = enter_rfmon_mode_wext(handle, sock_fd, device); - if (ret < 0) - return ret; /* error attempting to do so */ - if (ret == 1) - return 1; /* success */ -#endif /* IW_MODE_MONITOR */ - /* - * Either none of the mechanisms we know about work or none - * of those mechanisms are available, so we can't do monitor - * mode. + * We don't have libnl, so we can't do monitor mode. */ return 0; } +#endif /* HAVE_LIBNL */ #if defined(HAVE_LINUX_NET_TSTAMP_H) && defined(PACKET_TIMESTAMP) /* @@ -6564,23 +4801,29 @@ static const struct { /* * Set the list of time stamping types to include all types. */ -static void -iface_set_all_ts_types(pcap_t *handle) +static int +iface_set_all_ts_types(pcap_t *handle, char *ebuf) { u_int i; - handle->tstamp_type_count = NUM_SOF_TIMESTAMPING_TYPES; handle->tstamp_type_list = malloc(NUM_SOF_TIMESTAMPING_TYPES * sizeof(u_int)); + if (handle->tstamp_type_list == NULL) { + pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); + return -1; + } for (i = 0; i < NUM_SOF_TIMESTAMPING_TYPES; i++) handle->tstamp_type_list[i] = sof_ts_type_map[i].pcap_tstamp_val; + handle->tstamp_type_count = NUM_SOF_TIMESTAMPING_TYPES; + 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; @@ -6603,7 +4846,7 @@ iface_ethtool_get_ts_info(const char *device, pcap_t *handle, char *ebuf) /* * Create a socket from which to fetch time stamping capabilities. */ - fd = socket(PF_UNIX, SOCK_RAW, 0); + fd = get_if_ioctl_socket(); if (fd < 0) { pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, errno, "socket for SIOCETHTOOL(ETHTOOL_GET_TS_INFO)"); @@ -6628,7 +4871,8 @@ iface_ethtool_get_ts_info(const char *device, pcap_t *handle, char *ebuf) * asking for the time stamping types, so let's * just return all the possible types. */ - iface_set_all_ts_types(handle); + if (iface_set_all_ts_types(handle, ebuf) == -1) + return -1; return 0; case ENODEV: @@ -6666,6 +4910,8 @@ iface_ethtool_get_ts_info(const char *device, pcap_t *handle, char *ebuf) * 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; @@ -6676,15 +4922,20 @@ iface_ethtool_get_ts_info(const char *device, pcap_t *handle, char *ebuf) if (info.so_timestamping & sof_ts_type_map[i].soft_timestamping_val) num_ts_types++; } - handle->tstamp_type_count = num_ts_types; if (num_ts_types != 0) { handle->tstamp_type_list = malloc(num_ts_types * sizeof(u_int)); + if (handle->tstamp_type_list == NULL) { + pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); + return -1; + } for (i = 0, j = 0; i < NUM_SOF_TIMESTAMPING_TYPES; i++) { if (info.so_timestamping & sof_ts_type_map[i].soft_timestamping_val) { handle->tstamp_type_list[j] = sof_ts_type_map[i].pcap_tstamp_val; j++; } } + handle->tstamp_type_count = num_ts_types; } else handle->tstamp_type_list = NULL; @@ -6692,7 +4943,7 @@ iface_ethtool_get_ts_info(const char *device, pcap_t *handle, char *ebuf) } #else /* ETHTOOL_GET_TS_INFO */ static int -iface_ethtool_get_ts_info(const char *device, pcap_t *handle, char *ebuf _U_) +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 @@ -6710,14 +4961,22 @@ iface_ethtool_get_ts_info(const char *device, pcap_t *handle, char *ebuf _U_) * We don't have an ioctl to use to ask what's supported, * so say we support everything. */ - iface_set_all_ts_types(handle); + if (iface_set_all_ts_types(handle, ebuf) == -1) + return -1; 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) */ -#ifdef HAVE_PACKET_RING /* * Find out if we have any form of fragmentation/reassembly offloading. * @@ -6860,264 +5119,88 @@ iface_get_offload(pcap_t *handle _U_) } #endif /* SIOCETHTOOL */ -#endif /* HAVE_PACKET_RING */ - -#endif /* HAVE_PF_PACKET_SOCKETS */ - -/* ===== Functions to interface to the older kernels ================== */ - -/* - * Try to open a packet socket using the old kernel interface. - * Returns 1 on success and a PCAP_ERROR_ value on an error. - */ -static int -activate_old(pcap_t *handle) -{ - struct pcap_linux *handlep = handle->priv; - int err; - int arptype; - struct ifreq ifr; - const char *device = handle->opt.device; - struct utsname utsname; - int mtu; - +static struct dsa_proto { + const char *name; + bpf_u_int32 linktype; +} dsa_protos[] = { /* - * PF_INET/SOCK_PACKET sockets must be bound to a device, so we - * can't support the "any" device. + * None is special and indicates that the interface does not have + * any tagging protocol configured, and is therefore a standard + * Ethernet interface. */ - 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; - } + { "none", DLT_EN10MB }, + { "brcm", DLT_DSA_TAG_BRCM }, + { "brcm-prepend", DLT_DSA_TAG_BRCM_PREPEND }, + { "dsa", DLT_DSA_TAG_DSA }, + { "edsa", DLT_DSA_TAG_EDSA }, +}; - /* Open the socket */ - handle->fd = socket(PF_INET, SOCK_PACKET, htons(ETH_P_ALL)); - if (handle->fd == -1) { - err = errno; +static int +iface_dsa_get_proto_info(const char *device, pcap_t *handle) +{ + char *pathstr; + unsigned int i; + /* + * Make this significantly smaller than PCAP_ERRBUF_SIZE; + * the tag *shouldn't* have some huge long name, and making + * it smaller keeps newer versions of GCC from whining that + * the error message if we don't support the tag could + * overflow the error message buffer. + */ + char buf[128]; + ssize_t r; + int fd; + + fd = asprintf(&pathstr, "/sys/class/net/%s/dsa/tagging", device); + if (fd < 0) { pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, - err, "socket"); - if (err == EPERM || err == EACCES) { - /* - * You don't have permission to open the - * socket. - */ - return PCAP_ERROR_PERM_DENIED; - } else { - /* - * Other error. - */ - return PCAP_ERROR; - } - } - - /* It worked - we are using the old interface */ - handlep->sock_packet = 1; - - /* ...which means we get the link-layer header. */ - handlep->cooked = 0; - - /* Bind to the given device */ - if (iface_bind_old(handle->fd, device, handle->errbuf) == -1) - return PCAP_ERROR; - - /* - * Try to get the link-layer type. - */ - arptype = iface_get_arptype(handle->fd, device, handle->errbuf); - if (arptype < 0) - return PCAP_ERROR; - - /* - * Try to find the DLT_ type corresponding to that - * link-layer type. - */ - map_arphrd_to_dlt(handle, handle->fd, arptype, device, 0); - if (handle->linktype == -1) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, - "unknown arptype %d", arptype); + fd, "asprintf"); return PCAP_ERROR; } - /* Go to promisc mode if requested */ + fd = open(pathstr, O_RDONLY); + free(pathstr); + /* + * This is not fatal, kernel >= 4.20 *might* expose this attribute + */ + if (fd < 0) + return 0; - if (handle->opt.promisc) { - memset(&ifr, 0, sizeof(ifr)); - 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"); - return PCAP_ERROR; - } - if ((ifr.ifr_flags & IFF_PROMISC) == 0) { - /* - * Promiscuous mode isn't currently on, - * so turn it on, and remember that - * we should turn it off when the - * pcap_t is closed. - */ + r = read(fd, buf, sizeof(buf) - 1); + if (r <= 0) { + pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, + errno, "read"); + close(fd); + return PCAP_ERROR; + } + close(fd); - /* - * If we haven't already done so, arrange - * to have "pcap_close_all()" called when - * we exit. - */ - if (!pcap_do_addexit(handle)) { - /* - * "atexit()" failed; don't put - * the interface in promiscuous - * mode, just give up. - */ - return PCAP_ERROR; + /* + * Buffer should be LF terminated. + */ + if (buf[r - 1] == '\n') + r--; + buf[r] = '\0'; + + for (i = 0; i < sizeof(dsa_protos) / sizeof(dsa_protos[0]); i++) { + if (strlen(dsa_protos[i].name) == (size_t)r && + strcmp(buf, dsa_protos[i].name) == 0) { + handle->linktype = dsa_protos[i].linktype; + switch (dsa_protos[i].linktype) { + case DLT_EN10MB: + return 0; + default: + return 1; } - - ifr.ifr_flags |= IFF_PROMISC; - if (ioctl(handle->fd, SIOCSIFFLAGS, &ifr) == -1) { - pcap_fmt_errmsg_for_errno(handle->errbuf, - PCAP_ERRBUF_SIZE, errno, "SIOCSIFFLAGS"); - return PCAP_ERROR; - } - handlep->must_do_on_close |= MUST_CLEAR_PROMISC; - - /* - * Add this to the list of pcaps - * to close when we exit. - */ - pcap_add_to_pcaps_to_close(handle); } } - /* - * Compute the buffer size. - * - * We're using SOCK_PACKET, so this might be a 2.0[.x] - * kernel, and might require special handling - check. - */ - if (uname(&utsname) < 0 || - strncmp(utsname.release, "2.0", 3) == 0) { - /* - * Either we couldn't find out what kernel release - * this is, or it's a 2.0[.x] kernel. - * - * In the 2.0[.x] kernel, a "recvfrom()" on - * a SOCK_PACKET socket, with MSG_TRUNC set, will - * return the number of bytes read, so if we pass - * a length based on the snapshot length, it'll - * return the number of bytes from the packet - * copied to userland, not the actual length - * of the packet. - * - * This means that, for example, the IP dissector - * in tcpdump will get handed a packet length less - * than the length in the IP header, and will - * complain about "truncated-ip". - * - * So we don't bother trying to copy from the - * kernel only the bytes in which we're interested, - * but instead copy them all, just as the older - * versions of libpcap for Linux did. - * - * The buffer therefore needs to be big enough to - * hold the largest packet we can get from this - * device. Unfortunately, we can't get the MRU - * of the network; we can only get the MTU. The - * MTU may be too small, in which case a packet larger - * than the buffer size will be truncated *and* we - * won't get the actual packet size. - * - * However, if the snapshot length is larger than - * the buffer size based on the MTU, we use the - * snapshot length as the buffer size, instead; - * this means that with a sufficiently large snapshot - * length we won't artificially truncate packets - * to the MTU-based size. - * - * This mess just one of many problems with packet - * capture on 2.0[.x] kernels; you really want a - * 2.2[.x] or later kernel if you want packet capture - * to work well. - */ - mtu = iface_get_mtu(handle->fd, device, handle->errbuf); - if (mtu == -1) - return PCAP_ERROR; - handle->bufsize = MAX_LINKHEADER_SIZE + mtu; - if (handle->bufsize < (u_int)handle->snapshot) - handle->bufsize = (u_int)handle->snapshot; - } else { - /* - * This is a 2.2[.x] or later kernel. - * - * 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; - } + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "unsupported DSA tag: %s", buf); - /* - * Default value for offset to align link-layer payload - * on a 4-byte boundary. - */ - handle->offset = 0; - - /* - * SOCK_PACKET sockets don't supply information from - * stripped VLAN tags. - */ - handlep->vlan_offset = -1; /* unknown */ - - return 1; + return PCAP_ERROR; } -/* - * Bind the socket associated with FD to the given device using the - * interface of the old kernels. - */ -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)); - 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"); - return -1; - } - - /* Any pending errors, e.g., network is down? */ - - if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) { - pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, - errno, "getsockopt (SO_ERROR)"); - return -1; - } - - if (err > 0) { - pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, - err, "bind"); - return -1; - } - - return 0; -} - - -/* ===== System calls available on all supported kernels ============== */ - /* * Query the kernel for the MTU of the given interface. */ @@ -7157,21 +5240,25 @@ iface_get_arptype(int fd, const char *device, char *ebuf) 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; } return ifr.ifr_hwaddr.sa_family; } -#ifdef SO_ATTACH_FILTER static int -fix_program(pcap_t *handle, struct sock_fprog *fcode, int is_mmapped) +fix_program(pcap_t *handle, struct sock_fprog *fcode) { struct pcap_linux *handlep = handle->priv; size_t prog_size; @@ -7203,39 +5290,6 @@ fix_program(pcap_t *handle, struct sock_fprog *fcode, int is_mmapped) */ switch (BPF_CLASS(p->code)) { - case BPF_RET: - /* - * It's a return instruction; are we capturing - * in memory-mapped mode? - */ - if (!is_mmapped) { - /* - * No; is the snapshot length a constant, - * rather than the contents of the - * accumulator? - */ - if (BPF_MODE(p->code) == BPF_K) { - /* - * Yes - if the value to be returned, - * i.e. the snapshot length, is - * anything other than 0, make it - * MAXIMUM_SNAPLEN, so that the packet - * is truncated by "recvfrom()", - * not by the filter. - * - * XXX - there's nothing we can - * easily do if it's getting the - * value from the accumulator; we'd - * have to insert code to force - * non-zero values to be - * MAXIMUM_SNAPLEN. - */ - if (p->k != 0) - p->k = MAXIMUM_SNAPLEN; - } - } - break; - case BPF_LD: case BPF_LDX: /* @@ -7301,6 +5355,12 @@ fix_offset(pcap_t *handle, struct bpf_insn *p) * special magic kernel offset for that field. */ p->k = SKF_AD_OFF + SKF_AD_PROTOCOL; + } else if (p->k == 4) { + /* + * It's the ifindex field; map it to the + * special magic kernel offset for that field. + */ + p->k = SKF_AD_OFF + SKF_AD_IFINDEX; } else if (p->k == 10) { /* * It's the packet type field; map it to the @@ -7503,7 +5563,6 @@ reset_kernel_filter(pcap_t *handle) return -1; return 0; } -#endif int pcap_set_protocol_linux(pcap_t *p, int protocol) @@ -7520,15 +5579,9 @@ pcap_set_protocol_linux(pcap_t *p, int protocol) const char * pcap_lib_version(void) { -#ifdef HAVE_PACKET_RING - #if defined(HAVE_TPACKET3) +#if defined(HAVE_TPACKET3) return (PCAP_VERSION_STRING " (with TPACKET_V3)"); - #elif defined(HAVE_TPACKET2) - return (PCAP_VERSION_STRING " (with TPACKET_V2)"); - #else - return (PCAP_VERSION_STRING " (with TPACKET_V1)"); - #endif #else - return (PCAP_VERSION_STRING " (without TPACKET)"); + return (PCAP_VERSION_STRING " (with TPACKET_V2)"); #endif } diff --git a/pcap-netfilter-linux.c b/pcap-netfilter-linux.c index 91bad371b53d..33204a54e045 100644 --- a/pcap-netfilter-linux.c +++ b/pcap-netfilter-linux.c @@ -33,6 +33,7 @@ #endif #include "pcap-int.h" +#include "diag-control.h" #ifdef NEED_STRERROR_H #include "strerror.h" @@ -56,13 +57,13 @@ #include #include -/* NOTE: if your program drops privilages after pcap_activate() it WON'T work with nfqueue. +/* NOTE: if your program drops privileges after pcap_activate() it WON'T work with nfqueue. * It took me quite some time to debug ;/ * - * Sending any data to nfnetlink socket requires CAP_NET_ADMIN privilages, + * Sending any data to nfnetlink socket requires CAP_NET_ADMIN privileges, * and in nfqueue we need to send verdict reply after recving packet. * - * In tcpdump you can disable dropping privilages with -Z root + * In tcpdump you can disable dropping privileges with -Z root */ #include "pcap-netfilter-linux.h" @@ -91,7 +92,7 @@ netfilter_read_linux(pcap_t *handle, int max_packets, pcap_handler callback, u_c struct pcap_netfilter *handlep = handle->priv; register u_char *bp, *ep; int count = 0; - int len; + ssize_t len; /* * Has "pcap_breakloop()" been called? @@ -135,6 +136,13 @@ netfilter_read_linux(pcap_t *handle, int max_packets, pcap_handler callback, u_c bp = (unsigned char *)handle->buffer; } else bp = handle->bp; + + /* + * Loop through each message. + * + * This assumes that a single buffer of message will have + * <= INT_MAX packets, so the message count doesn't overflow. + */ ep = bp + len; while (bp < ep) { const struct nlmsghdr *nlh = (const struct nlmsghdr *) bp; @@ -152,14 +160,25 @@ netfilter_read_linux(pcap_t *handle, int max_packets, pcap_handler callback, u_c */ if (handle->break_loop) { handle->bp = bp; - handle->cc = ep - bp; + handle->cc = (int)(ep - bp); if (count == 0) { handle->break_loop = 0; return PCAP_ERROR_BREAK; } else return count; } - if (ep - bp < NLMSG_SPACE(0)) { + /* + * NLMSG_SPACE(0) might be signed or might be unsigned, + * depending on whether the kernel defines NLMSG_ALIGNTO + * as 4, which older kernels do, or as 4U, which newer + * kernels do. + * + * ep - bp is of type ptrdiff_t, which is signed. + * + * To squelch warnings, we cast both to size_t, which + * is unsigned; ep >= bp, so the cast is safe. + */ + if ((size_t)(ep - bp) < (size_t)NLMSG_SPACE(0)) { /* * There's less than one netlink message left * in the buffer. Give up. @@ -168,7 +187,7 @@ netfilter_read_linux(pcap_t *handle, int max_packets, pcap_handler callback, u_c } if (nlh->nlmsg_len < sizeof(struct nlmsghdr) || (u_int)len < nlh->nlmsg_len) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Message truncated: (got: %d) (nlmsg_len: %u)", len, nlh->nlmsg_len); + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Message truncated: (got: %zd) (nlmsg_len: %u)", len, nlh->nlmsg_len); return -1; } @@ -190,7 +209,7 @@ netfilter_read_linux(pcap_t *handle, int max_packets, pcap_handler callback, u_c const struct nfattr *payload_attr = NULL; if (nlh->nlmsg_len < HDR_LENGTH) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Malformed message: (nlmsg_len: %u)", nlh->nlmsg_len); + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Malformed message: (nlmsg_len: %u)", nlh->nlmsg_len); return -1; } @@ -240,7 +259,7 @@ netfilter_read_linux(pcap_t *handle, int max_packets, pcap_handler callback, u_c gettimeofday(&pkth.ts, NULL); if (handle->fcode.bf_insns == NULL || - bpf_filter(handle->fcode.bf_insns, payload, pkth.len, pkth.caplen)) + pcap_filter(handle->fcode.bf_insns, payload, pkth.len, pkth.caplen)) { handlep->packets_read++; callback(user, &pkth, payload); @@ -262,14 +281,21 @@ netfilter_read_linux(pcap_t *handle, int max_packets, pcap_handler callback, u_c * If the message length would run past the end of the * buffer, truncate it to the remaining space in the * buffer. + * + * To squelch warnings, we cast ep - bp to uint32_t, which + * is unsigned and is the type of msg_len; ep >= bp, and + * len should fit in 32 bits (either it's set from an int + * or it's set from a recv() call with a buffer size that's + * an int, and we're assuming either ILP32 or LP64), so + * the cast is safe. */ - if (msg_len > ep - bp) - msg_len = ep - bp; + if (msg_len > (uint32_t)(ep - bp)) + msg_len = (uint32_t)(ep - bp); bp += msg_len; if (count >= max_packets && !PACKET_COUNT_IS_UNLIMITED(max_packets)) { handle->bp = bp; - handle->cc = ep - bp; + handle->cc = (int)(ep - bp); if (handle->cc < 0) handle->cc = 0; return count; @@ -299,9 +325,9 @@ netfilter_stats_linux(pcap_t *handle, struct pcap_stat *stats) } static int -netfilter_inject_linux(pcap_t *handle, const void *buf _U_, size_t size _U_) +netfilter_inject_linux(pcap_t *handle, const void *buf _U_, int size _U_) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Packet injection is not supported on netfilter devices"); return (-1); } @@ -325,7 +351,9 @@ netfilter_send_config_msg(const pcap_t *handle, uint16_t msg_type, int ack, u_in static unsigned int seq_id; if (!seq_id) +DIAG_OFF_NARROWING seq_id = time(NULL); +DIAG_ON_NARROWING ++seq_id; nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nfgenmsg)); @@ -363,7 +391,11 @@ netfilter_send_config_msg(const pcap_t *handle, uint16_t msg_type, int ack, u_in /* ignore interrupt system call error */ do { - len = recvfrom(handle->fd, buf, sizeof(buf), 0, (struct sockaddr *) &snl, &addrlen); + /* + * The buffer is not so big that its size won't + * fit into an int. + */ + len = (int)recvfrom(handle->fd, buf, sizeof(buf), 0, (struct sockaddr *) &snl, &addrlen); } while ((len == -1) && (errno == EINTR)); if (len <= 0) @@ -510,7 +542,7 @@ netfilter_activate(pcap_t* handle) char *end_dev; if (group_count == 32) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Maximum 32 netfilter groups! dev: %s", handle->opt.device); return PCAP_ERROR; @@ -519,7 +551,7 @@ netfilter_activate(pcap_t* handle) group_id = strtol(dev, &end_dev, 0); if (end_dev != dev) { if (group_id < 0 || group_id > 65535) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Netfilter group range from 0 to 65535 (got %ld)", group_id); return PCAP_ERROR; @@ -535,7 +567,7 @@ netfilter_activate(pcap_t* handle) } if (type == OTHER || *dev) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't get netfilter group(s) index from %s", handle->opt.device); return PCAP_ERROR; @@ -616,7 +648,7 @@ netfilter_activate(pcap_t* handle) if (nflog_send_config_cmd(handle, groups[i], NFULNL_CFG_CMD_BIND, AF_UNSPEC) < 0) { pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, - "Can't listen on group group index"); + "Can't listen on group index"); goto close_fail; } @@ -646,7 +678,7 @@ netfilter_activate(pcap_t* handle) if (nfqueue_send_config_cmd(handle, groups[i], NFQNL_CFG_CMD_BIND, AF_UNSPEC) < 0) { pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, errno, - "Can't listen on group group index"); + "Can't listen on group index"); goto close_fail; } @@ -721,7 +753,7 @@ netfilter_create(const char *device, char *ebuf, int *is_ours) /* OK, it's probably ours. */ *is_ours = 1; - p = pcap_create_common(ebuf, sizeof (struct pcap_netfilter)); + p = PCAP_CREATE_COMMON(ebuf, struct pcap_netfilter); if (p == NULL) return (NULL); diff --git a/pcap-netmap.c b/pcap-netmap.c index b2301a7fd8de..27d36e5bb6d3 100644 --- a/pcap-netmap.c +++ b/pcap-netmap.c @@ -29,7 +29,6 @@ #endif #include -#include #include #include #include @@ -82,7 +81,7 @@ pcap_netmap_filter(u_char *arg, struct pcap_pkthdr *h, const u_char *buf) const struct bpf_insn *pc = p->fcode.bf_insns; ++pn->rx_pkts; - if (pc == NULL || bpf_filter(pc, buf, h->len, h->caplen)) + if (pc == NULL || pcap_filter(pc, buf, h->len, h->caplen)) pn->cb(pn->cb_arg, h, buf); } @@ -117,7 +116,7 @@ pcap_netmap_dispatch(pcap_t *p, int cnt, pcap_handler cb, u_char *user) /* XXX need to check the NIOCTXSYNC/poll */ static int -pcap_netmap_inject(pcap_t *p, const void *buf, size_t size) +pcap_netmap_inject(pcap_t *p, const void *buf, int size) { struct pcap_netmap *pn = p->priv; struct nm_desc *d = pn->d; @@ -287,7 +286,7 @@ pcap_netmap_create(const char *device, char *ebuf, int *is_ours) *is_ours = (!strncmp(device, "netmap:", 7) || !strncmp(device, "vale", 4)); if (! *is_ours) return NULL; - p = pcap_create_common(ebuf, sizeof (struct pcap_netmap)); + p = PCAP_CREATE_COMMON(ebuf, struct pcap_netmap); if (p == NULL) return (NULL); p->activate_op = pcap_netmap_activate; diff --git a/pcap-new.c b/pcap-new.c index e61cf6ab2e9b..76388a998fd5 100644 --- a/pcap-new.c +++ b/pcap-new.c @@ -36,6 +36,7 @@ #endif #include "ftmacros.h" +#include "diag-control.h" /* * sockutils.h may include on Windows, and pcap-int.h will @@ -88,7 +89,7 @@ int pcap_findalldevs_ex(const char *source, struct pcap_rmtauth *auth, pcap_if_t if (strlen(source) > PCAP_BUF_SIZE) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "The source string is too long. Cannot handle it correctly."); + snprintf(errbuf, PCAP_ERRBUF_SIZE, "The source string is too long. Cannot handle it correctly."); return -1; } @@ -119,7 +120,7 @@ int pcap_findalldevs_ex(const char *source, struct pcap_rmtauth *auth, pcap_if_t if (*alldevs == NULL) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "No interfaces found! Make sure libpcap/Npcap is properly installed" " on the local machine."); return -1; @@ -209,7 +210,7 @@ int pcap_findalldevs_ex(const char *source, struct pcap_rmtauth *auth, pcap_if_t } /* Save the path for future reference */ - pcap_snprintf(path, sizeof(path), "%s", name); + snprintf(path, sizeof(path), "%s", name); pathlen = strlen(path); #ifdef _WIN32 @@ -224,7 +225,7 @@ int pcap_findalldevs_ex(const char *source, struct pcap_rmtauth *auth, pcap_if_t if (filehandle == INVALID_HANDLE_VALUE) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error when listing files: does folder '%s' exist?", path); + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error when listing files: does folder '%s' exist?", path); return -1; } @@ -237,7 +238,10 @@ int pcap_findalldevs_ex(const char *source, struct pcap_rmtauth *auth, pcap_if_t if (filedata == NULL) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error when listing files: does folder '%s' exist?", path); + DIAG_OFF_FORMAT_TRUNCATION + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error when listing files: does folder '%s' exist?", path); + DIAG_ON_FORMAT_TRUNCATION + closedir(unixdir); return -1; } #endif @@ -249,11 +253,13 @@ int pcap_findalldevs_ex(const char *source, struct pcap_rmtauth *auth, pcap_if_t /* Skip the file if the pathname won't fit in the buffer */ if (pathlen + strlen(filedata.cFileName) >= sizeof(filename)) continue; - pcap_snprintf(filename, sizeof(filename), "%s%s", path, filedata.cFileName); + snprintf(filename, sizeof(filename), "%s%s", path, filedata.cFileName); #else if (pathlen + strlen(filedata->d_name) >= sizeof(filename)) continue; - pcap_snprintf(filename, sizeof(filename), "%s%s", path, filedata->d_name); + DIAG_OFF_FORMAT_TRUNCATION + snprintf(filename, sizeof(filename), "%s%s", path, filedata->d_name); + DIAG_ON_FORMAT_TRUNCATION #endif fp = pcap_open_offline(filename, errbuf); @@ -268,6 +274,11 @@ int pcap_findalldevs_ex(const char *source, struct pcap_rmtauth *auth, pcap_if_t PCAP_ERRBUF_SIZE, errno, "malloc() failed"); pcap_freealldevs(*alldevs); +#ifdef _WIN32 + FindClose(filehandle); +#else + closedir(unixdir); +#endif return -1; } @@ -297,6 +308,11 @@ int pcap_findalldevs_ex(const char *source, struct pcap_rmtauth *auth, pcap_if_t if (pcap_createsrcstr(tmpstring, PCAP_SRC_FILE, NULL, NULL, filename, errbuf) == -1) { pcap_freealldevs(*alldevs); +#ifdef _WIN32 + FindClose(filehandle); +#else + closedir(unixdir); +#endif return -1; } @@ -307,6 +323,11 @@ int pcap_findalldevs_ex(const char *source, struct pcap_rmtauth *auth, pcap_if_t PCAP_ERRBUF_SIZE, errno, "malloc() failed"); pcap_freealldevs(*alldevs); +#ifdef _WIN32 + FindClose(filehandle); +#else + closedir(unixdir); +#endif return -1; } @@ -321,6 +342,11 @@ int pcap_findalldevs_ex(const char *source, struct pcap_rmtauth *auth, pcap_if_t PCAP_ERRBUF_SIZE, errno, "malloc() failed"); pcap_freealldevs(*alldevs); +#ifdef _WIN32 + FindClose(filehandle); +#else + closedir(unixdir); +#endif return -1; } @@ -334,9 +360,11 @@ int pcap_findalldevs_ex(const char *source, struct pcap_rmtauth *auth, pcap_if_t #endif -#ifdef _WIN32 /* Close the search handle. */ +#ifdef _WIN32 FindClose(filehandle); +#else + closedir(unixdir); #endif return 0; @@ -370,7 +398,7 @@ pcap_t *pcap_open(const char *source, int snaplen, int flags, int read_timeout, if (strlen(source) > PCAP_BUF_SIZE) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "The source string is too long. Cannot handle it correctly."); + snprintf(errbuf, PCAP_ERRBUF_SIZE, "The source string is too long. Cannot handle it correctly."); return NULL; } @@ -444,17 +472,19 @@ pcap_t *pcap_open(const char *source, int snaplen, int flags, int read_timeout, return fp; fail: + DIAG_OFF_FORMAT_TRUNCATION if (status == PCAP_ERROR) - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s", + snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s", name, fp->errbuf); else if (status == PCAP_ERROR_NO_SUCH_DEVICE || status == PCAP_ERROR_PERM_DENIED || status == PCAP_ERROR_PROMISC_PERM_DENIED) - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s (%s)", + snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s (%s)", name, pcap_statustostr(status), fp->errbuf); else - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s", + snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s", name, pcap_statustostr(status)); + DIAG_ON_FORMAT_TRUNCATION pcap_close(fp); return NULL; } diff --git a/pcap-nit.c b/pcap-nit.c index 6a1a77c24c68..6f4f8dd87d0a 100644 --- a/pcap-nit.c +++ b/pcap-nit.c @@ -43,7 +43,6 @@ #include #include -#include #include #include @@ -126,6 +125,9 @@ pcap_read_nit(pcap_t *p, int cnt, pcap_handler callback, u_char *user) * Loop through each packet. The increment expression * rounds up to the next int boundary past the end of * the previous packet. + * + * This assumes that a single buffer of packets will have + * <= INT_MAX packets, so the packet count doesn't overflow. */ n = 0; ep = bp + cc; @@ -168,7 +170,7 @@ pcap_read_nit(pcap_t *p, int cnt, pcap_handler callback, u_char *user) continue; default: - pcap_snprintf(p->errbuf, sizeof(p->errbuf), + snprintf(p->errbuf, sizeof(p->errbuf), "bad nit state %d", nh->nh_state); return (-1); } @@ -179,7 +181,7 @@ pcap_read_nit(pcap_t *p, int cnt, pcap_handler callback, u_char *user) caplen = nh->nh_wirelen; if (caplen > p->snapshot) caplen = p->snapshot; - if (bpf_filter(p->fcode.bf_insns, cp, nh->nh_wirelen, caplen)) { + if (pcap_filter(p->fcode.bf_insns, cp, nh->nh_wirelen, caplen)) { struct pcap_pkthdr h; h.ts = nh->nh_timestamp; h.len = nh->nh_wirelen; @@ -197,7 +199,7 @@ pcap_read_nit(pcap_t *p, int cnt, pcap_handler callback, u_char *user) } static int -pcap_inject_nit(pcap_t *p, const void *buf, size_t size) +pcap_inject_nit(pcap_t *p, const void *buf, int size) { struct sockaddr sa; int ret; @@ -371,7 +373,7 @@ pcap_create_interface(const char *device _U_, char *ebuf) { pcap_t *p; - p = pcap_create_common(ebuf, sizeof (struct pcap_nit)); + p = PCAP_CREATE_COMMON(ebuf, struct pcap_nit); if (p == NULL) return (NULL); diff --git a/pcap-npf.c b/pcap-npf.c index da4641f379c8..62c526d923ad 100644 --- a/pcap-npf.c +++ b/pcap-npf.c @@ -36,11 +36,23 @@ #endif #include +#include /* for INT_MAX */ #define PCAP_DONT_INCLUDE_PCAP_BPF_H #include #include #include +/* + * XXX - Packet32.h defines bpf_program, so we can't include + * , which also defines it; that's why we define + * PCAP_DONT_INCLUDE_PCAP_BPF_H, + * + * However, no header in the WinPcap or Npcap SDKs defines the + * macros for BPF code, so we have to define them ourselves. + */ +#define BPF_RET 0x06 +#define BPF_K 0x00 + /* Old-school MinGW have these headers in a different place. */ #if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) @@ -55,6 +67,10 @@ #include #endif /* HAVE_DAG_API */ +#include "diag-control.h" + +#include "pcap-airpcap.h" + static int pcap_setfilter_npf(pcap_t *, struct bpf_program *); static int pcap_setfilter_win32_dag(pcap_t *, struct bpf_program *); static int pcap_getnonblock_npf(pcap_t *); @@ -155,7 +171,7 @@ oid_get_request(ADAPTER *adapter, bpf_u_int32 oid, void *data, size_t *lenp, */ oid_data_arg = malloc(sizeof (PACKET_OID_DATA) + *lenp); if (oid_data_arg == NULL) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Couldn't allocate argument buffer for PacketRequest"); return (PCAP_ERROR); } @@ -240,13 +256,13 @@ pcap_stats_npf(pcap_t *p, struct pcap_stat *ps) * have an API that returns data in a form like the Options section of a * pcapng Interface Statistics Block: * - * http://xml2rfc.tools.ietf.org/cgi-bin/xml2rfc.cgi?url=https://raw.githubusercontent.com/pcapng/pcapng/master/draft-tuexen-opsawg-pcapng.xml&modeAsFormat=html/ascii&type=ascii#rfc.section.4.6 + * https://xml2rfc.tools.ietf.org/cgi-bin/xml2rfc.cgi?url=https://raw.githubusercontent.com/pcapng/pcapng/master/draft-tuexen-opsawg-pcapng.xml&modeAsFormat=html/ascii&type=ascii#rfc.section.4.6 * * which would let us add new statistics straightforwardly and indicate which * statistics we are and are *not* providing, rather than having to provide * possibly-bogus values for statistics we can't provide. */ -struct pcap_stat * +static struct pcap_stat * pcap_stats_ex_npf(pcap_t *p, int *pcap_stat_size) { struct pcap_win *pw = p->priv; @@ -269,7 +285,12 @@ pcap_stats_ex_npf(pcap_t *p, int *pcap_stat_size) p->stat.ps_recv = bstats.bs_recv; p->stat.ps_drop = bstats.bs_drop; p->stat.ps_ifdrop = bstats.ps_ifdrop; -#ifdef ENABLE_REMOTE + /* + * Just in case this is ever compiled for a target other than + * Windows, which is somewhere between extremely unlikely and + * impossible. + */ +#ifdef _WIN32 p->stat.ps_capt = bstats.bs_capt; #endif return (&p->stat); @@ -283,7 +304,7 @@ pcap_setbuff_npf(pcap_t *p, int dim) if(PacketSetBuff(pw->adapter,dim)==FALSE) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "driver error: not enough memory to allocate the kernel buffer"); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "driver error: not enough memory to allocate the kernel buffer"); return (-1); } return (0); @@ -297,7 +318,7 @@ pcap_setmode_npf(pcap_t *p, int mode) if(PacketSetMode(pw->adapter,mode)==FALSE) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "driver error: working mode not recognized"); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "driver error: working mode not recognized"); return (-1); } @@ -312,7 +333,7 @@ pcap_setmintocopy_npf(pcap_t *p, int size) if(PacketSetMinToCopy(pw->adapter, size)==FALSE) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "driver error: unable to set the requested mintocopy size"); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "driver error: unable to set the requested mintocopy size"); return (-1); } return (0); @@ -350,7 +371,7 @@ pcap_oid_set_request_npf(pcap_t *p, bpf_u_int32 oid, const void *data, */ oid_data_arg = malloc(sizeof (PACKET_OID_DATA) + *lenp); if (oid_data_arg == NULL) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Couldn't allocate argument buffer for PacketRequest"); return (PCAP_ERROR); } @@ -383,12 +404,6 @@ pcap_sendqueue_transmit_npf(pcap_t *p, pcap_send_queue *queue, int sync) struct pcap_win *pw = p->priv; u_int res; - if (pw->adapter==NULL) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "Cannot transmit a queue to an offline capture or to a TurboCap port"); - return (0); - } - res = PacketSendPackets(pw->adapter, queue->buffer, queue->len, @@ -396,7 +411,7 @@ pcap_sendqueue_transmit_npf(pcap_t *p, pcap_send_queue *queue, int sync) if(res != queue->len){ pcap_fmt_errmsg_for_win32_err(p->errbuf, PCAP_ERRBUF_SIZE, - GetLastError(), "Error opening adapter"); + GetLastError(), "Error queueing packets"); } return (res); @@ -409,7 +424,7 @@ pcap_setuserbuffer_npf(pcap_t *p, int size) if (size<=0) { /* Bogus parameter */ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Error: invalid size %d",size); return (-1); } @@ -418,7 +433,7 @@ pcap_setuserbuffer_npf(pcap_t *p, int size) new_buff=(unsigned char*)malloc(sizeof(char)*size); if (!new_buff) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Error: not enough memory"); return (-1); } @@ -431,6 +446,31 @@ pcap_setuserbuffer_npf(pcap_t *p, int size) return (0); } +#ifdef HAVE_NPCAP_PACKET_API +/* + * Kernel dump mode isn't supported in Npcap; calls to PacketSetDumpName(), + * PacketSetDumpLimits(), and PacketIsDumpEnded() will get compile-time + * deprecation warnings. + * + * Avoid calling them; just return errors indicating that kernel dump + * mode isn't supported in Npcap. + */ +static int +pcap_live_dump_npf(pcap_t *p, char *filename _U_, int maxsize _U_, + int maxpacks _U_) +{ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Npcap doesn't support kernel dump mode"); + return (-1); +} +static int +pcap_live_dump_ended_npf(pcap_t *p, int sync) +{ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Npcap doesn't support kernel dump mode"); + return (-1); +} +#else /* HAVE_NPCAP_PACKET_API */ static int pcap_live_dump_npf(pcap_t *p, char *filename, int maxsize, int maxpacks) { @@ -440,7 +480,7 @@ pcap_live_dump_npf(pcap_t *p, char *filename, int maxsize, int maxpacks) /* Set the packet driver in dump mode */ res = PacketSetMode(pw->adapter, PACKET_MODE_DUMP); if(res == FALSE){ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Error setting dump mode"); return (-1); } @@ -448,7 +488,7 @@ pcap_live_dump_npf(pcap_t *p, char *filename, int maxsize, int maxpacks) /* Set the name of the dump file */ res = PacketSetDumpName(pw->adapter, filename, (int)strlen(filename)); if(res == FALSE){ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Error setting kernel dump file name"); return (-1); } @@ -456,8 +496,8 @@ pcap_live_dump_npf(pcap_t *p, char *filename, int maxsize, int maxpacks) /* Set the limits of the dump file */ res = PacketSetDumpLimits(pw->adapter, maxsize, maxpacks); if(res == FALSE) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "Error setting dump limit"); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Error setting dump limit"); return (-1); } @@ -471,31 +511,36 @@ pcap_live_dump_ended_npf(pcap_t *p, int sync) return (PacketIsDumpEnded(pw->adapter, (BOOLEAN)sync)); } +#endif /* HAVE_NPCAP_PACKET_API */ +#ifdef HAVE_AIRPCAP_API static PAirpcapHandle pcap_get_airpcap_handle_npf(pcap_t *p) { -#ifdef HAVE_AIRPCAP_API struct pcap_win *pw = p->priv; return (PacketGetAirPcapHandle(pw->adapter)); -#else - return (NULL); -#endif /* HAVE_AIRPCAP_API */ } +#else /* HAVE_AIRPCAP_API */ +static PAirpcapHandle +pcap_get_airpcap_handle_npf(pcap_t *p _U_) +{ + return (NULL); +} +#endif /* HAVE_AIRPCAP_API */ static int pcap_read_npf(pcap_t *p, int cnt, pcap_handler callback, u_char *user) { PACKET Packet; int cc; - int n = 0; + int n; register u_char *bp, *ep; u_char *datap; struct pcap_win *pw = p->priv; cc = p->cc; - if (p->cc == 0) { + if (cc == 0) { /* * Has "pcap_breakloop()" been called? */ @@ -525,27 +570,53 @@ pcap_read_npf(pcap_t *p, int cnt, pcap_handler callback, u_char *user) if (!PacketReceivePacket(pw->adapter, &Packet, TRUE)) { /* * Did the device go away? - * If so, the error we get is ERROR_GEN_FAILURE. + * If so, the error we get can either be + * ERROR_GEN_FAILURE or ERROR_DEVICE_REMOVED. */ DWORD errcode = GetLastError(); - if (errcode == ERROR_GEN_FAILURE) { + if (errcode == ERROR_GEN_FAILURE || + errcode == ERROR_DEVICE_REMOVED) { /* * The device on which we're capturing * went away, or it became unusable * by NPF due to a suspend/resume. * + * ERROR_GEN_FAILURE comes from + * STATUS_UNSUCCESSFUL, as well as some + * other NT status codes that the Npcap + * driver is unlikely to return. * XXX - hopefully no other error * conditions are indicated by this. * + * ERROR_DEVICE_REMOVED comes from + * STATUS_DEVICE_REMOVED. + * + * We report the Windows status code + * name and the corresponding NT status + * code name, for the benefit of attempts + * to debug cases where this error is + * reported when the device *wasn't* + * removed, either because it's not + * removable, it's removable but wasn't + * removed, or it's a device that doesn't + * correspond to a physical device. + * * XXX - we really should return an * appropriate error for that, but * pcap_dispatch() etc. aren't * documented as having error returns * other than PCAP_ERROR or PCAP_ERROR_BREAK. */ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "The interface disappeared"); + const char *errcode_msg; + + if (errcode == ERROR_GEN_FAILURE) + errcode_msg = "ERROR_GEN_FAILURE/STATUS_UNSUCCESSFUL"; + else + errcode_msg = "ERROR_DEVICE_REMOVED/STATUS_DEVICE_REMOVED"; + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "The interface disappeared (error code %s)", + errcode_msg); } else { pcap_fmt_errmsg_for_win32_err(p->errbuf, PCAP_ERRBUF_SIZE, errcode, @@ -563,11 +634,15 @@ pcap_read_npf(pcap_t *p, int cnt, pcap_handler callback, u_char *user) /* * Loop through each packet. + * + * This assumes that a single buffer of packets will have + * <= INT_MAX packets, so the packet count doesn't overflow. */ #define bhp ((struct bpf_hdr *)bp) + n = 0; ep = bp + cc; for (;;) { - register int caplen, hdrlen; + register u_int caplen, hdrlen; /* * Has "pcap_breakloop()" been called? @@ -601,13 +676,13 @@ pcap_read_npf(pcap_t *p, int cnt, pcap_handler callback, u_char *user) * in kernel, no need to do it now - we already know * the packet passed the filter. * - * XXX - bpf_filter() should always return TRUE if + * XXX - pcap_filter() should always return TRUE if * handed a null pointer for the program, but it might * just try to "run" the filter, so we check here. */ if (pw->filtering_in_kernel || p->fcode.bf_insns == NULL || - bpf_filter(p->fcode.bf_insns, datap, bhp->bh_datalen, caplen)) { + pcap_filter(p->fcode.bf_insns, datap, bhp->bh_datalen, caplen)) { #ifdef ENABLE_REMOTE switch (p->rmt_samp.method) { @@ -706,7 +781,7 @@ pcap_read_win32_dag(pcap_t *p, int cnt, pcap_handler callback, u_char *user) */ PacketInitPacket(&Packet, (BYTE *)p->buffer, p->bufsize); if (!PacketReceivePacket(pw->adapter, &Packet, TRUE)) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "read error: PacketReceivePacket failed"); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "read error: PacketReceivePacket failed"); return (-1); } @@ -721,6 +796,21 @@ pcap_read_win32_dag(pcap_t *p, int cnt, pcap_handler callback, u_char *user) endofbuf = (char*)header + cc; + /* + * 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(cnt)) + cnt = INT_MAX; + /* * Cycle through the packets */ @@ -811,10 +901,10 @@ pcap_read_win32_dag(pcap_t *p, int cnt, pcap_handler callback, u_char *user) } } - /* No underlaying filtering system. We need to filter on our own */ + /* No underlying filtering system. We need to filter on our own */ if (p->fcode.bf_insns) { - if (bpf_filter(p->fcode.bf_insns, dp, packet_len, caplen) == 0) + if (pcap_filter(p->fcode.bf_insns, dp, packet_len, caplen) == 0) { /* Move to next packet */ header = (dag_record_t*)((char*)header + erf_record_len); @@ -822,7 +912,7 @@ pcap_read_win32_dag(pcap_t *p, int cnt, pcap_handler callback, u_char *user) } } - /* Fill the header for the user suppplied callback function */ + /* Fill the header for the user supplied callback function */ pcap_header.caplen = caplen; pcap_header.len = packet_len; @@ -848,14 +938,15 @@ pcap_read_win32_dag(pcap_t *p, int cnt, pcap_handler callback, u_char *user) /* Send a packet to the network */ static int -pcap_inject_npf(pcap_t *p, const void *buf, size_t size) +pcap_inject_npf(pcap_t *p, const void *buf, int size) { struct pcap_win *pw = p->priv; PACKET pkt; PacketInitPacket(&pkt, (PVOID)buf, size); if(PacketSendPacket(pw->adapter,&pkt,TRUE) == FALSE) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send error: PacketSendPacket failed"); + pcap_fmt_errmsg_for_win32_err(p->errbuf, PCAP_ERRBUF_SIZE, + GetLastError(), "send error: PacketSendPacket failed"); return (-1); } @@ -864,7 +955,7 @@ pcap_inject_npf(pcap_t *p, const void *buf, size_t size) * "pcap_inject()" is expected to return the number of bytes * sent. */ - return ((int)size); + return (size); } static void @@ -883,6 +974,48 @@ pcap_cleanup_npf(pcap_t *p) pcap_cleanup_live_common(p); } +static void +pcap_breakloop_npf(pcap_t *p) +{ + pcap_breakloop_common(p); + struct pcap_win *pw = p->priv; + + /* XXX - what if this fails? */ + SetEvent(PacketGetReadEvent(pw->adapter)); +} + +/* + * These are NTSTATUS values: + * + * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/87fba13e-bf06-450e-83b1-9241dc81e781 + * + * with the "Customer" bit set. If a driver returns them, they are not + * mapped to Windows error values in userland; they're returned by + * GetLastError(). + * + * Note that "driver" here includes the Npcap NPF driver, as various + * versions would take NT status values and set the "Customer" bit + * before returning the status code. The commit message for the + * change that started doing that is + * + * Returned a customer-defined NTSTATUS in OID requests to avoid + * NTSTATUS-to-Win32 Error code translation. + * + * but I don't know why the goal was to avoid that translation. + * + * Attempting to set the hardware filter on a Microsoft Surface Pro's + * Mobile Broadband Adapter returns an error that appears to be + * NDIS_STATUS_NOT_SUPPORTED ORed with the "Customer" bit, so it's + * probably indicating that it doesn't support that. + * + * It is likely that there are other devices which throw spurious errors, + * at which point this will need refactoring to efficiently check against + * a list, but for now we can just check this one value. Perhaps the + * right way to do this is compare against various NDIS errors with + * the "customer" bit ORed in. + */ +#define NT_STATUS_CUSTOMER_DEFINED 0x20000000 + static int pcap_activate_npf(pcap_t *p) { @@ -890,6 +1023,8 @@ pcap_activate_npf(pcap_t *p) NetType type; int res; int status = 0; + struct bpf_insn total_insn; + struct bpf_program total_prog; if (p->opt.rfmon) { /* @@ -921,7 +1056,7 @@ pcap_activate_npf(pcap_t *p) } } - /* Init WinSock */ + /* Init Winsock if it hasn't already been initialized */ pcap_wsockinit(); pw->adapter = PacketOpenAdapter(p->opt.device); @@ -933,12 +1068,30 @@ pcap_activate_npf(pcap_t *p) /* * What error did we get when trying to open the adapter? */ - if (errcode == ERROR_BAD_UNIT) { + switch (errcode) { + + case ERROR_BAD_UNIT: /* * There's no such device. + * There's nothing to add, so clear the error + * message. */ + p->errbuf[0] = '\0'; return (PCAP_ERROR_NO_SUCH_DEVICE); - } else { + + case ERROR_ACCESS_DENIED: + /* + * There is, but we don't have permission to + * use it. + * + * XXX - we currently get ERROR_BAD_UNIT if the + * user says "no" to the UAC prompt. + */ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "The helper program for \"Admin-only Mode\" must be allowed to make changes to your device"); + return (PCAP_ERROR_PERM_DENIED); + + default: /* * Unknown - report details. */ @@ -963,10 +1116,9 @@ pcap_activate_npf(pcap_t *p) /*Set the linktype*/ switch (type.LinkType) { - case NdisMediumWan: - p->linktype = DLT_EN10MB; - break; - + /* + * NDIS-defined medium types. + */ case NdisMedium802_3: p->linktype = DLT_EN10MB; /* @@ -990,12 +1142,19 @@ pcap_activate_npf(pcap_t *p) } break; + case NdisMedium802_5: + /* + * Token Ring. + */ + p->linktype = DLT_IEEE802; + break; + case NdisMediumFddi: p->linktype = DLT_FDDI; break; - case NdisMedium802_5: - p->linktype = DLT_IEEE802; + case NdisMediumWan: + p->linktype = DLT_EN10MB; break; case NdisMediumArcnetRaw: @@ -1010,6 +1169,21 @@ pcap_activate_npf(pcap_t *p) p->linktype = DLT_ATM_RFC1483; break; + case NdisMediumWirelessWan: + p->linktype = DLT_RAW; + break; + + case NdisMediumIP: + p->linktype = DLT_RAW; + break; + + /* + * Npcap-defined medium types. + */ + case NdisMediumNull: + p->linktype = DLT_NULL; + break; + case NdisMediumCHDLC: p->linktype = DLT_CHDLC; break; @@ -1018,10 +1192,6 @@ pcap_activate_npf(pcap_t *p) p->linktype = DLT_PPP_SERIAL; break; - case NdisMediumNull: - p->linktype = DLT_NULL; - break; - case NdisMediumBare80211: p->linktype = DLT_IEEE802_11; break; @@ -1034,12 +1204,6 @@ pcap_activate_npf(pcap_t *p) p->linktype = DLT_PPI; break; -#ifdef NdisMediumWirelessWan - case NdisMediumWirelessWan: - p->linktype = DLT_RAW; - break; -#endif - default: /* * An unknown medium type is assumed to supply Ethernet @@ -1053,13 +1217,72 @@ pcap_activate_npf(pcap_t *p) * some programs will report the warning. */ p->linktype = DLT_EN10MB; - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Unknown NdisMedium value %d, defaulting to DLT_EN10MB", type.LinkType); status = PCAP_WARNING; break; } +#ifdef HAVE_PACKET_GET_TIMESTAMP_MODES + /* + * Set the timestamp type. + * (Yes, we require PacketGetTimestampModes(), not just + * PacketSetTimestampMode(). If we have the former, we + * have the latter, unless somebody's using a version + * of Npcap that they've hacked to provide the former + * but not the latter; if they've done that, either + * they're confused or they're trolling us.) + */ + switch (p->opt.tstamp_type) { + + case PCAP_TSTAMP_HOST_HIPREC_UNSYNCED: + /* + * Better than low-res, but *not* synchronized with + * the OS clock. + */ + if (!PacketSetTimestampMode(pw->adapter, TIMESTAMPMODE_SINGLE_SYNCHRONIZATION)) + { + pcap_fmt_errmsg_for_win32_err(p->errbuf, PCAP_ERRBUF_SIZE, + GetLastError(), "Cannot set the time stamp mode to TIMESTAMPMODE_SINGLE_SYNCHRONIZATION"); + goto bad; + } + break; + + case PCAP_TSTAMP_HOST_LOWPREC: + /* + * Low-res, but synchronized with the OS clock. + */ + if (!PacketSetTimestampMode(pw->adapter, TIMESTAMPMODE_QUERYSYSTEMTIME)) + { + pcap_fmt_errmsg_for_win32_err(p->errbuf, PCAP_ERRBUF_SIZE, + GetLastError(), "Cannot set the time stamp mode to TIMESTAMPMODE_QUERYSYSTEMTIME"); + goto bad; + } + break; + + case PCAP_TSTAMP_HOST_HIPREC: + /* + * High-res, and synchronized with the OS clock. + */ + if (!PacketSetTimestampMode(pw->adapter, TIMESTAMPMODE_QUERYSYSTEMTIME_PRECISE)) + { + pcap_fmt_errmsg_for_win32_err(p->errbuf, PCAP_ERRBUF_SIZE, + GetLastError(), "Cannot set the time stamp mode to TIMESTAMPMODE_QUERYSYSTEMTIME_PRECISE"); + goto bad; + } + break; + + case PCAP_TSTAMP_HOST: + /* + * XXX - do whatever the default is, for now. + * Set to the highest resolution that's synchronized + * with the system clock? + */ + break; + } +#endif /* HAVE_PACKET_GET_TIMESTAMP_MODES */ + /* * Turn a negative snapshot value (invalid), a snapshot value of * 0 (unspecified), or a value bigger than the normal maximum @@ -1077,17 +1300,66 @@ pcap_activate_npf(pcap_t *p) if (PacketSetHwFilter(pw->adapter,NDIS_PACKET_TYPE_PROMISCUOUS) == FALSE) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "failed to set hardware filter to promiscuous mode"); - goto bad; + DWORD errcode = GetLastError(); + + /* + * Suppress spurious error generated by non-compiant + * MS Surface mobile adapters that appear to + * return NDIS_STATUS_NOT_SUPPORTED for attempts + * to set the hardware filter. + * + * It appears to be reporting NDIS_STATUS_NOT_SUPPORTED, + * but with the NT status value "Customer" bit set; + * the Npcap NPF driver sets that bit in some cases. + * + * If we knew that this meant "promiscuous mode + * isn't supported", we could add a "promiscuous + * mode isn't supported" error code and return + * that, but: + * + * 1) we don't know that it means that + * rather than meaning "we reject attempts + * to set the filter, even though the NDIS + * specifications say you shouldn't do that" + * + * and + * + * 2) other interface types that don't + * support promiscuous mode, at least + * on UN*Xes, just silently ignore + * attempts to set promiscuous mode + * + * and rejecting it with an error could disrupt + * attempts to capture, as many programs (tcpdump, + * *shark) default to promiscuous mode. + * + * Alternatively, we could return the "promiscuous + * mode not supported" *warning* value, so that + * correct code will either ignore it or report + * it and continue capturing. (This may require + * a pcap_init() flag to request that return + * value, so that old incorrect programs that + * assume a non-zero return from pcap_activate() + * is an error don't break.) + */ + if (errcode != (NDIS_STATUS_NOT_SUPPORTED|NT_STATUS_CUSTOMER_DEFINED)) + { + pcap_fmt_errmsg_for_win32_err(p->errbuf, + PCAP_ERRBUF_SIZE, errcode, + "failed to set hardware filter to promiscuous mode"); + goto bad; + } } } else { - /* NDIS_PACKET_TYPE_ALL_LOCAL selects "All packets sent by installed - * protocols and all packets indicated by the NIC" but if no protocol - * drivers (like TCP/IP) are installed, NDIS_PACKET_TYPE_DIRECTED, - * NDIS_PACKET_TYPE_BROADCAST, and NDIS_PACKET_TYPE_MULTICAST are needed to - * capture incoming frames. + /* + * NDIS_PACKET_TYPE_ALL_LOCAL selects "All packets sent by + * installed protocols and all packets indicated by the NIC", + * but if no protocol drivers (like TCP/IP) are installed, + * NDIS_PACKET_TYPE_DIRECTED, NDIS_PACKET_TYPE_BROADCAST, + * and NDIS_PACKET_TYPE_MULTICAST are needed to capture + * incoming frames. */ if (PacketSetHwFilter(pw->adapter, NDIS_PACKET_TYPE_ALL_LOCAL | @@ -1095,8 +1367,19 @@ pcap_activate_npf(pcap_t *p) NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_MULTICAST) == FALSE) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "failed to set hardware filter to non-promiscuous mode"); - goto bad; + DWORD errcode = GetLastError(); + + /* + * Suppress spurious error generated by non-compiant + * MS Surface mobile adapters. + */ + if (errcode != (NDIS_STATUS_NOT_SUPPORTED|NT_STATUS_CUSTOMER_DEFINED)) + { + pcap_fmt_errmsg_for_win32_err(p->errbuf, + PCAP_ERRBUF_SIZE, errcode, + "failed to set hardware filter to non-promiscuous mode"); + goto bad; + } } } @@ -1112,12 +1395,12 @@ pcap_activate_npf(pcap_t *p) * If the buffer size wasn't explicitly set, default to * WIN32_DEFAULT_KERNEL_BUFFER_SIZE. */ - if (p->opt.buffer_size == 0) - p->opt.buffer_size = WIN32_DEFAULT_KERNEL_BUFFER_SIZE; + if (p->opt.buffer_size == 0) + p->opt.buffer_size = WIN32_DEFAULT_KERNEL_BUFFER_SIZE; if(PacketSetBuff(pw->adapter,p->opt.buffer_size)==FALSE) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "driver error: not enough memory to allocate the kernel buffer"); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "driver error: not enough memory to allocate the kernel buffer"); goto bad; } @@ -1166,7 +1449,7 @@ pcap_activate_npf(pcap_t *p) int postype = 0; char keyname[512]; - pcap_snprintf(keyname, sizeof(keyname), "%s\\CardParams\\%s", + snprintf(keyname, sizeof(keyname), "%s\\CardParams\\%s", "SYSTEM\\CurrentControlSet\\Services\\DAG", strstr(_strlwr(p->opt.device), "dag")); do @@ -1205,6 +1488,29 @@ pcap_activate_npf(pcap_t *p) #endif /* HAVE_DAG_API */ } + /* + * If there's no filter program installed, there's + * no indication to the kernel of what the snapshot + * length should be, so no snapshotting is done. + * + * Therefore, when we open the device, we install + * an "accept everything" filter with the specified + * snapshot length. + */ + total_insn.code = (u_short)(BPF_RET | BPF_K); + total_insn.jt = 0; + total_insn.jf = 0; + total_insn.k = p->snapshot; + + total_prog.bf_len = 1; + total_prog.bf_insns = &total_insn; + if (!PacketSetBpf(pw->adapter, &total_prog)) { + pcap_fmt_errmsg_for_win32_err(p->errbuf, PCAP_ERRBUF_SIZE, + GetLastError(), "PacketSetBpf"); + status = PCAP_ERROR; + goto bad; + } + PacketSetReadTimeout(pw->adapter, p->opt.timeout); /* disable loopback capture if requested */ @@ -1212,7 +1518,7 @@ pcap_activate_npf(pcap_t *p) { if (!PacketSetLoopbackBehavior(pw->adapter, NPF_DISABLE_LOOPBACK)) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Unable to disable the capture of loopback packets."); goto bad; } @@ -1241,6 +1547,7 @@ pcap_activate_npf(pcap_t *p) p->getnonblock_op = pcap_getnonblock_npf; p->setnonblock_op = pcap_setnonblock_npf; p->stats_op = pcap_stats_npf; + p->breakloop_op = pcap_breakloop_npf; p->stats_ex_op = pcap_stats_ex_npf; p->setbuff_op = pcap_setbuff_npf; p->setmode_op = pcap_setmode_npf; @@ -1283,17 +1590,295 @@ pcap_can_set_rfmon_npf(pcap_t *p) return (PacketIsMonitorModeSupported(p->opt.device) == 1); } +/* + * Get a list of time stamp types. + */ +#ifdef HAVE_PACKET_GET_TIMESTAMP_MODES +static int +get_ts_types(const char *device, pcap_t *p, char *ebuf) +{ + char *device_copy = NULL; + ADAPTER *adapter = NULL; + ULONG num_ts_modes; + BOOL ret; + DWORD error = ERROR_SUCCESS; + ULONG *modes = NULL; + int status = 0; + + do { + /* + * First, find out how many time stamp modes we have. + * To do that, we have to open the adapter. + * + * XXX - PacketOpenAdapter() takes a non-const pointer + * as an argument, so we make a copy of the argument and + * pass that to it. + */ + device_copy = strdup(device); + if (device_copy == NULL) { + pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, errno, "malloc"); + status = -1; + break; + } + + adapter = PacketOpenAdapter(device_copy); + if (adapter == NULL) + { + error = GetLastError(); + /* + * If we can't open the device now, we won't be + * able to later, either. + * + * If the error is something that indicates + * that the device doesn't exist, or that they + * don't have permission to open the device - or + * perhaps that they don't have permission to get + * a list of devices, if PacketOpenAdapter() does + * that - the user will find that out when they try + * to activate the device; just return an empty + * list of time stamp types. + * + * Treating either of those as errors will, for + * example, cause "tcpdump -i " to fail, + * because it first tries to pass the interface + * name to pcap_create() and pcap_activate(), + * in order to handle OSes where interfaces can + * have names that are just numbers (stand up + * and say hello, Linux!), and, if pcap_activate() + * fails with a "no such device" error, checks + * whether the interface name is a valid number + * and, if so, tries to use it as an index in + * the list of interfaces. + * + * That means pcap_create() must succeed even + * for interfaces that don't exist, with the + * failure occurring at pcap_activate() time. + */ + if (error == ERROR_BAD_UNIT || + error == ERROR_ACCESS_DENIED) { + p->tstamp_type_count = 0; + p->tstamp_type_list = NULL; + status = 0; + } else { + pcap_fmt_errmsg_for_win32_err(ebuf, + PCAP_ERRBUF_SIZE, error, + "Error opening adapter"); + status = -1; + } + break; + } + + /* + * Get the total number of time stamp modes. + * + * The buffer for PacketGetTimestampModes() is + * a sequence of 1 or more ULONGs. What's + * passed to PacketGetTimestampModes() should have + * the total number of ULONGs in the first ULONG; + * what's returned *from* PacketGetTimestampModes() + * has the total number of time stamp modes in + * the first ULONG. + * + * Yes, that means if there are N time stamp + * modes, the first ULONG should be set to N+1 + * on input, and will be set to N on output. + * + * We first make a call to PacketGetTimestampModes() + * with a pointer to a single ULONG set to 1; the + * call should fail with ERROR_MORE_DATA (unless + * there are *no* modes, but that should never + * happen), and that ULONG should be set to the + * number of modes. + */ + num_ts_modes = 1; + ret = PacketGetTimestampModes(adapter, &num_ts_modes); + if (!ret) { + /* + * OK, it failed. Did it fail with + * ERROR_MORE_DATA? + */ + error = GetLastError(); + if (error != ERROR_MORE_DATA) { + /* + * No, did it fail with ERROR_INVALID_FUNCTION? + */ + if (error == ERROR_INVALID_FUNCTION) { + /* + * This is probably due to + * the driver with which Packet.dll + * communicates being older, or + * being a WinPcap driver, so + * that it doesn't support + * BIOCGTIMESTAMPMODES. + * + * Tell the user to try uninstalling + * Npcap - and WinPcap if installed - + * and re-installing it, to flush + * out all older drivers. + */ + snprintf(ebuf, PCAP_ERRBUF_SIZE, + "PacketGetTimestampModes() failed with ERROR_INVALID_FUNCTION; try uninstalling Npcap, and WinPcap if installed, and re-installing it from npcap.com"); + status = -1; + break; + } + + /* + * No, some other error. Fail. + */ + pcap_fmt_errmsg_for_win32_err(ebuf, + PCAP_ERRBUF_SIZE, error, + "Error calling PacketGetTimestampModes"); + status = -1; + break; + } + } + /* else (ret == TRUE) + * Unexpected success. Let's act like we got ERROR_MORE_DATA. + * If it doesn't work, we'll hit some other error condition farther on. + */ + + /* If the driver reports no modes supported *and* + * ERROR_MORE_DATA, something is seriously wrong. + * We *could* ignore the error and continue without supporting + * settable timestamp modes, but that would hide a bug. + */ + if (num_ts_modes == 0) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, + "PacketGetTimestampModes() reports 0 modes supported."); + status = -1; + break; + } + + /* + * Yes, so we now know how many types to fetch. + * + * The buffer needs to have one ULONG for the + * count and num_ts_modes ULONGs for the + * num_ts_modes time stamp types. + */ + modes = (ULONG *)malloc((1 + num_ts_modes) * sizeof(ULONG)); + if (modes == NULL) { + /* Out of memory. */ + pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, errno, "malloc"); + status = -1; + break; + } + modes[0] = 1 + num_ts_modes; + if (!PacketGetTimestampModes(adapter, modes)) { + pcap_fmt_errmsg_for_win32_err(ebuf, + PCAP_ERRBUF_SIZE, GetLastError(), + "Error calling PacketGetTimestampModes"); + status = -1; + break; + } + if (modes[0] != num_ts_modes) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, + "First PacketGetTimestampModes() call gives %lu modes, second call gives %lu modes", + num_ts_modes, modes[0]); + status = -1; + break; + } + + /* + * Allocate a buffer big enough for + * PCAP_TSTAMP_HOST (default) plus + * the explicitly specified modes. + */ + p->tstamp_type_list = malloc((1 + num_ts_modes) * sizeof(u_int)); + if (p->tstamp_type_list == NULL) { + pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, errno, "malloc"); + status = -1; + break; + } + u_int num_ts_types = 0; + p->tstamp_type_list[num_ts_types] = + PCAP_TSTAMP_HOST; + num_ts_types++; + for (ULONG i = 0; i < num_ts_modes; i++) { + switch (modes[i + 1]) { + + case TIMESTAMPMODE_SINGLE_SYNCHRONIZATION: + /* + * Better than low-res, + * but *not* synchronized + * with the OS clock. + */ + p->tstamp_type_list[num_ts_types] = + PCAP_TSTAMP_HOST_HIPREC_UNSYNCED; + num_ts_types++; + break; + + case TIMESTAMPMODE_QUERYSYSTEMTIME: + /* + * Low-res, but synchronized + * with the OS clock. + */ + p->tstamp_type_list[num_ts_types] = + PCAP_TSTAMP_HOST_LOWPREC; + num_ts_types++; + break; + + case TIMESTAMPMODE_QUERYSYSTEMTIME_PRECISE: + /* + * High-res, and synchronized + * with the OS clock. + */ + p->tstamp_type_list[num_ts_types] = + PCAP_TSTAMP_HOST_HIPREC; + num_ts_types++; + break; + + default: + /* + * Unknown, so we can't + * report it. + */ + break; + } + } + p->tstamp_type_count = num_ts_types; + } while (0); + + /* Clean up temporary allocations */ + if (device_copy != NULL) { + free(device_copy); + } + if (modes != NULL) { + free(modes); + } + if (adapter != NULL) { + PacketCloseAdapter(adapter); + } + + return status; +} +#else /* HAVE_PACKET_GET_TIMESTAMP_MODES */ +static int +get_ts_types(const char *device _U_, pcap_t *p _U_, char *ebuf _U_) +{ + /* + * Nothing to fetch, so it always "succeeds". + */ + return 0; +} +#endif /* HAVE_PACKET_GET_TIMESTAMP_MODES */ + pcap_t * pcap_create_interface(const char *device _U_, char *ebuf) { pcap_t *p; - p = pcap_create_common(ebuf, sizeof(struct pcap_win)); + p = PCAP_CREATE_COMMON(ebuf, struct pcap_win); if (p == NULL) return (NULL); p->activate_op = pcap_activate_npf; p->can_set_rfmon_op = pcap_can_set_rfmon_npf; + + if (get_ts_types(device, p, ebuf) == -1) { + pcap_close(p); + return (NULL); + } return (p); } @@ -1356,7 +1941,7 @@ pcap_setfilter_npf(pcap_t *p, struct bpf_program *fp) } /* - * We filter at user level, since the kernel driver does't process the packets + * We filter at user level, since the kernel driver doesn't process the packets */ static int pcap_setfilter_win32_dag(pcap_t *p, struct bpf_program *fp) { @@ -1554,7 +2139,7 @@ get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf) *flags |= PCAP_IF_WIRELESS; /* - * A "network assosiation state" makes no sense for airpcap. + * A "network association state" makes no sense for airpcap. */ *flags |= PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE; PacketCloseAdapter(adapter); @@ -1596,6 +2181,12 @@ get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf) * running. */ break; + + default: + /* + * Unknown. + */ + break; } } else { /* @@ -1632,7 +2223,13 @@ get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf) if (status == 0) { /* * We got the physical medium. + * + * XXX - we might want to check for NdisPhysicalMediumWiMax + * and NdisPhysicalMediumNative802_15_4 being + * part of the enum, and check for those in the "wireless" + * case. */ +DIAG_OFF_ENUM_SWITCH switch (phys_medium) { case NdisPhysicalMediumWirelessLan: @@ -1649,10 +2246,11 @@ get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf) default: /* - * Not wireless. + * Not wireless or unknown */ break; } +DIAG_ON_ENUM_SWITCH } #endif @@ -1683,6 +2281,13 @@ get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf) */ *flags |= PCAP_IF_CONNECTION_STATUS_DISCONNECTED; break; + + case MediaConnectStateUnknown: + default: + /* + * It's unknown whether it's connected or not. + */ + break; } } #else @@ -1765,7 +2370,7 @@ pcap_platform_finddevs(pcap_if_list_t *devlistp, char *errbuf) AdaptersName = (char*) malloc(NameLength); if (AdaptersName == NULL) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Cannot allocate enough memory to list the adapters."); + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Cannot allocate enough memory to list the adapters."); return (-1); } @@ -1806,6 +2411,20 @@ pcap_platform_finddevs(pcap_if_list_t *devlistp, char *errbuf) name = &AdaptersName[0]; while (*name != '\0') { bpf_u_int32 flags = 0; + +#ifdef HAVE_AIRPCAP_API + /* + * Is this an AirPcap device? + * If so, ignore it; it'll get added later, by the + * AirPcap code. + */ + if (device_is_airpcap(name, errbuf) == 1) { + name += strlen(name) + 1; + desc += strlen(desc) + 1; + continue; + } +#endif + #ifdef HAVE_PACKET_IS_LOOPBACK_ADAPTER /* * Is this a loopback interface? @@ -1861,10 +2480,26 @@ pcap_lookupdev(char *errbuf) DWORD dwVersion; DWORD dwWindowsMajorVersion; -#pragma warning (push) -#pragma warning (disable: 4996) /* disable MSVC's GetVersion() deprecated warning here */ + /* + * We disable this in "new API" mode, because 1) in WinPcap/Npcap, + * it may return UTF-16 strings, for backwards-compatibility + * reasons, and we're also disabling the hack to make that work, + * for not-going-past-the-end-of-a-string reasons, and 2) we + * want its behavior to be consistent. + * + * In addition, it's not thread-safe, so we've marked it as + * deprecated. + */ + if (pcap_new_api) { + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "pcap_lookupdev() is deprecated and is not supported in programs calling pcap_init()"); + return (NULL); + } + +/* disable MSVC's GetVersion() deprecated warning here */ +DIAG_OFF_DEPRECATION dwVersion = GetVersion(); /* get the OS version */ -#pragma warning (pop) +DIAG_ON_DEPRECATION dwWindowsMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion))); if (dwVersion >= 0x80000000 && dwWindowsMajorVersion >= 4) { @@ -1895,7 +2530,7 @@ pcap_lookupdev(char *errbuf) if(TAdaptersName == NULL) { - (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "memory allocation failure"); + (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, "memory allocation failure"); return NULL; } @@ -2056,7 +2691,7 @@ pcap_lib_version(void) /* * Generate the version string. */ - char *packet_version_string = PacketGetVersion(); + const char *packet_version_string = PacketGetVersion(); if (strcmp(WINPCAP_VER_STRING, packet_version_string) == 0) { /* diff --git a/pcap-pf.c b/pcap-pf.c index fde97bacd05e..bd27933eff69 100644 --- a/pcap-pf.c +++ b/pcap-pf.c @@ -48,7 +48,6 @@ struct rtentry; #include #include -#include #include #include #include @@ -104,9 +103,7 @@ pcap_read_pf(pcap_t *pc, int cnt, pcap_handler callback, u_char *user) register u_char *p, *bp; register int cc, n, buflen, inc; register struct enstamp *sp; -#ifdef LBL_ALIGN struct enstamp stamp; -#endif register u_int pad; again: @@ -136,6 +133,9 @@ pcap_read_pf(pcap_t *pc, int cnt, pcap_handler callback, u_char *user) bp = pc->bp; /* * Loop through each packet. + * + * This assumes that a single buffer of packets will have + * <= INT_MAX packets, so the packet count doesn't overflow. */ n = 0; pad = pc->fddipad; @@ -160,19 +160,17 @@ pcap_read_pf(pcap_t *pc, int cnt, pcap_handler callback, u_char *user) } } if (cc < sizeof(*sp)) { - pcap_snprintf(pc->errbuf, sizeof(pc->errbuf), + snprintf(pc->errbuf, sizeof(pc->errbuf), "pf short read (%d)", cc); return (-1); } -#ifdef LBL_ALIGN if ((long)bp & 3) { sp = &stamp; memcpy((char *)sp, (char *)bp, sizeof(*sp)); } else -#endif sp = (struct enstamp *)bp; if (sp->ens_stamplen != sizeof(*sp)) { - pcap_snprintf(pc->errbuf, sizeof(pc->errbuf), + snprintf(pc->errbuf, sizeof(pc->errbuf), "pf short stamplen (%d)", sp->ens_stamplen); return (-1); @@ -205,7 +203,7 @@ pcap_read_pf(pcap_t *pc, int cnt, pcap_handler callback, u_char *user) * skipping that padding. */ if (pf->filtering_in_kernel || - bpf_filter(pc->fcode.bf_insns, p, sp->ens_count, buflen)) { + pcap_filter(pc->fcode.bf_insns, p, sp->ens_count, buflen)) { struct pcap_pkthdr h; pf->TotAccepted++; h.ts = sp->ens_tstamp; @@ -226,7 +224,7 @@ pcap_read_pf(pcap_t *pc, int cnt, pcap_handler callback, u_char *user) } static int -pcap_inject_pf(pcap_t *p, const void *buf, size_t size) +pcap_inject_pf(pcap_t *p, const void *buf, int size) { int ret; @@ -261,7 +259,7 @@ pcap_stats_pf(pcap_t *p, struct pcap_stat *ps) * full. * * "ps_ifdrop" counts packets dropped by the network - * inteface (regardless of whether they would have passed + * interface (regardless of whether they would have passed * the input filter, of course). * * If packet filtering is not being done in the kernel: @@ -273,7 +271,7 @@ pcap_stats_pf(pcap_t *p, struct pcap_stat *ps) * the userland filter. * * "ps_ifdrop" counts packets dropped by the network - * inteface (regardless of whether they would have passed + * interface (regardless of whether they would have passed * the input filter, of course). * * These statistics don't include packets not yet read from @@ -331,7 +329,7 @@ pcap_activate_pf(pcap_t *p) p->fd = pfopen(p->opt.device, O_RDONLY); if (p->fd < 0) { if (errno == EACCES) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "pf open: %s: Permission denied\n" "your system may not be properly configured; see the packetfilter(4) man page", p->opt.device); @@ -464,7 +462,7 @@ pcap_activate_pf(pcap_t *p) * framing", there's not much we can do, as that * doesn't specify a particular type of header. */ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "unknown data-link type %u", devparams.end_dev_type); err = PCAP_ERROR; goto bad; @@ -540,7 +538,7 @@ pcap_create_interface(const char *device _U_, char *ebuf) { pcap_t *p; - p = pcap_create_common(ebuf, sizeof (struct pcap_pf)); + p = PCAP_CREATE_COMMON(ebuf, struct pcap_pf); if (p == NULL) return (NULL); diff --git a/pcap-rdmasniff.c b/pcap-rdmasniff.c index c50fe3fd693f..d63ca898877b 100644 --- a/pcap-rdmasniff.c +++ b/pcap-rdmasniff.c @@ -38,6 +38,7 @@ #include #include #include +#include /* for INT_MAX */ #include #if !defined(IBV_FLOW_ATTR_SNIFFER) @@ -57,7 +58,7 @@ struct pcap_rdmasniff { struct ibv_flow * flow; struct ibv_mr * mr; u_char * oneshot_buffer; - unsigned port_num; + unsigned long port_num; int cq_event; u_int packets_recv; }; @@ -136,15 +137,30 @@ rdmasniff_read(pcap_t *handle, int max_packets, pcap_handler callback, u_char *u priv->cq_event = 1; } - while (count < 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 (count < max_packets) { if (ibv_poll_cq(priv->cq, 1, &wc) != 1) { priv->cq_event = 0; break; } if (wc.status != IBV_WC_SUCCESS) { - fprintf(stderr, "failed WC wr_id %lld status %d/%s\n", - (unsigned long long) wc.wr_id, + fprintf(stderr, "failed WC wr_id %" PRIu64 " status %d/%s\n", + wc.wr_id, wc.status, ibv_wc_status_str(wc.status)); continue; } @@ -156,7 +172,7 @@ rdmasniff_read(pcap_t *handle, int max_packets, pcap_handler callback, u_char *u pktd = (u_char *) handle->buffer + wc.wr_id * RDMASNIFF_RECEIVE_SIZE; if (handle->fcode.bf_insns == NULL || - bpf_filter(handle->fcode.bf_insns, pktd, pkth.len, pkth.caplen)) { + pcap_filter(handle->fcode.bf_insns, pktd, pkth.len, pkth.caplen)) { callback(user, &pkth, pktd); ++priv->packets_recv; ++count; @@ -197,21 +213,21 @@ rdmasniff_activate(pcap_t *handle) priv->context = ibv_open_device(priv->rdma_device); if (!priv->context) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Failed to open device %s", handle->opt.device); goto error; } priv->pd = ibv_alloc_pd(priv->context); if (!priv->pd) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Failed to alloc PD for device %s", handle->opt.device); goto error; } priv->channel = ibv_create_comp_channel(priv->context); if (!priv->channel) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Failed to create comp channel for device %s", handle->opt.device); goto error; } @@ -219,7 +235,7 @@ rdmasniff_activate(pcap_t *handle) priv->cq = ibv_create_cq(priv->context, RDMASNIFF_NUM_RECEIVES, NULL, priv->channel, 0); if (!priv->cq) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Failed to create CQ for device %s", handle->opt.device); goto error; } @@ -233,7 +249,7 @@ rdmasniff_activate(pcap_t *handle) qp_init_attr.qp_type = IBV_QPT_RAW_PACKET; priv->qp = ibv_create_qp(priv->pd, &qp_init_attr); if (!priv->qp) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Failed to create QP for device %s", handle->opt.device); goto error; } @@ -242,7 +258,7 @@ rdmasniff_activate(pcap_t *handle) qp_attr.qp_state = IBV_QPS_INIT; qp_attr.port_num = priv->port_num; if (ibv_modify_qp(priv->qp, &qp_attr, IBV_QP_STATE | IBV_QP_PORT)) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Failed to modify QP to INIT for device %s", handle->opt.device); goto error; } @@ -250,7 +266,7 @@ rdmasniff_activate(pcap_t *handle) memset(&qp_attr, 0, sizeof qp_attr); qp_attr.qp_state = IBV_QPS_RTR; if (ibv_modify_qp(priv->qp, &qp_attr, IBV_QP_STATE)) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Failed to modify QP to RTR for device %s", handle->opt.device); goto error; } @@ -261,7 +277,7 @@ rdmasniff_activate(pcap_t *handle) flow_attr.port = priv->port_num; priv->flow = ibv_create_flow(priv->qp, &flow_attr); if (!priv->flow) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Failed to create flow for device %s", handle->opt.device); goto error; } @@ -269,21 +285,21 @@ rdmasniff_activate(pcap_t *handle) handle->bufsize = RDMASNIFF_NUM_RECEIVES * RDMASNIFF_RECEIVE_SIZE; handle->buffer = malloc(handle->bufsize); if (!handle->buffer) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Failed to allocate receive buffer for device %s", handle->opt.device); goto error; } priv->oneshot_buffer = malloc(RDMASNIFF_RECEIVE_SIZE); if (!priv->oneshot_buffer) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Failed to allocate oneshot buffer for device %s", handle->opt.device); goto error; } priv->mr = ibv_reg_mr(priv->pd, handle->buffer, handle->bufsize, IBV_ACCESS_LOCAL_WRITE); if (!priv->mr) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Failed to register MR for device %s", handle->opt.device); goto error; } @@ -361,14 +377,18 @@ rdmasniff_create(const char *device, char *ebuf, int *is_ours) int numdev; size_t namelen; const char *port; - unsigned port_num; + unsigned long port_num; int i; pcap_t *p = NULL; *is_ours = 0; dev_list = ibv_get_device_list(&numdev); - if (!dev_list || !numdev) { + if (!dev_list) { + return NULL; + } + if (!numdev) { + ibv_free_device_list(dev_list); return NULL; } @@ -391,7 +411,7 @@ rdmasniff_create(const char *device, char *ebuf, int *is_ours) !strncmp(device, dev_list[i]->name, namelen)) { *is_ours = 1; - p = pcap_create_common(ebuf, sizeof (struct pcap_rdmasniff)); + p = PCAP_CREATE_COMMON(ebuf, struct pcap_rdmasniff); if (p) { p->activate_op = rdmasniff_activate; priv = p->priv; @@ -415,7 +435,7 @@ rdmasniff_findalldevs(pcap_if_list_t *devlistp, char *err_str) int ret = 0; dev_list = ibv_get_device_list(&numdev); - if (!dev_list || !numdev) { + if (!dev_list) { return 0; } @@ -426,11 +446,10 @@ rdmasniff_findalldevs(pcap_if_list_t *devlistp, char *err_str) */ if (!add_dev(devlistp, dev_list[i]->name, 0, "RDMA sniffer", err_str)) { ret = -1; - goto out; + break; } } -out: ibv_free_device_list(dev_list); return ret; } diff --git a/pcap-rpcap.c b/pcap-rpcap.c index 705f06f2ad87..22fc736355b6 100644 --- a/pcap-rpcap.c +++ b/pcap-rpcap.c @@ -36,16 +36,27 @@ #endif #include "ftmacros.h" +#include "diag-control.h" #include /* for strlen(), ... */ #include /* for malloc(), free(), ... */ #include /* for functions with variable number of arguments */ #include /* for the errno variable */ +#include /* for INT_MAX */ #include "sockutils.h" #include "pcap-int.h" +#include "pcap-util.h" #include "rpcap-protocol.h" #include "pcap-rpcap.h" +#ifdef _WIN32 +#include "charconv.h" /* for utf_8_to_acp_truncated() */ +#endif + +#ifdef HAVE_OPENSSL +#include "sslutils.h" +#endif + /* * This file contains the pcap module for capturing from a remote machine's * interfaces using the RPCAP protocol. @@ -83,7 +94,9 @@ struct activehosts { struct sockaddr_storage host; SOCKET sockctrl; + SSL *ssl; uint8 protocol_version; + int byte_swapped; struct activehosts *next; }; @@ -97,6 +110,7 @@ static struct activehosts *activeHosts; * pcap_remoteact_cleanup() for more details. */ static SOCKET sockmain; +static SSL *ssl_main; /* * Private data for capturing remotely using the rpcap protocol. @@ -111,11 +125,14 @@ struct pcap_rpcap { SOCKET rmt_sockctrl; /* socket ID of the socket used for the control connection */ SOCKET rmt_sockdata; /* socket ID of the socket used for the data connection */ + SSL *ctrl_ssl, *data_ssl; /* optional transport of rmt_sockctrl and rmt_sockdata via TLS */ int rmt_flags; /* we have to save flags, since they are passed by the pcap_open_live(), but they are used by the pcap_startcapture() */ int rmt_capstarted; /* 'true' if the capture is already started (needed to knoe if we have to call the pcap_startcapture() */ char *currentfilter; /* Pointer to a buffer (allocated at run-time) that stores the current filter. Needed when flag PCAP_OPENFLAG_NOCAPTURE_RPCAP is turned on. */ uint8 protocol_version; /* negotiated protocol version */ + uint8 uses_ssl; /* User asked for rpcaps scheme */ + int byte_swapped; /* Server byte order is swapped from ours */ unsigned int TotNetDrops; /* keeps the number of packets that have been dropped by the network */ @@ -155,14 +172,14 @@ static void pcap_save_current_filter_rpcap(pcap_t *fp, const char *filter); static int pcap_setfilter_rpcap(pcap_t *fp, struct bpf_program *prog); static int pcap_setsampling_remote(pcap_t *fp); static int pcap_startcapture_remote(pcap_t *fp); -static int rpcap_recv_msg_header(SOCKET sock, struct rpcap_header *header, char *errbuf); -static int rpcap_check_msg_ver(SOCKET sock, uint8 expected_ver, struct rpcap_header *header, char *errbuf); -static int rpcap_check_msg_type(SOCKET sock, uint8 request_type, struct rpcap_header *header, uint16 *errcode, char *errbuf); -static int rpcap_process_msg_header(SOCKET sock, uint8 ver, uint8 request_type, struct rpcap_header *header, char *errbuf); -static int rpcap_recv(SOCKET sock, void *buffer, size_t toread, uint32 *plen, char *errbuf); -static void rpcap_msg_err(SOCKET sockctrl, uint32 plen, char *remote_errbuf); -static int rpcap_discard(SOCKET sock, uint32 len, char *errbuf); -static int rpcap_read_packet_msg(SOCKET sock, pcap_t *p, size_t size); +static int rpcap_recv_msg_header(SOCKET sock, SSL *, struct rpcap_header *header, char *errbuf); +static int rpcap_check_msg_ver(SOCKET sock, SSL *, uint8 expected_ver, struct rpcap_header *header, char *errbuf); +static int rpcap_check_msg_type(SOCKET sock, SSL *, uint8 request_type, struct rpcap_header *header, uint16 *errcode, char *errbuf); +static int rpcap_process_msg_header(SOCKET sock, SSL *, uint8 ver, uint8 request_type, struct rpcap_header *header, char *errbuf); +static int rpcap_recv(SOCKET sock, SSL *, void *buffer, size_t toread, uint32 *plen, char *errbuf); +static void rpcap_msg_err(SOCKET sockctrl, SSL *, uint32 plen, char *remote_errbuf); +static int rpcap_discard(SOCKET sock, SSL *, uint32 len, char *errbuf); +static int rpcap_read_packet_msg(struct pcap_rpcap const *, pcap_t *p, size_t size); /**************************************************** * * @@ -375,7 +392,7 @@ static int pcap_read_nocb_remote(pcap_t *p, struct pcap_pkthdr *pkt_header, u_ch struct rpcap_pkthdr *net_pkt_header; /* header of the packet, from the message */ u_char *net_pkt_data; /* packet data from the message */ uint32 plen; - int retval; /* generic return value */ + int retval = 0; /* generic return value */ int msglen; /* Structures needed for the select() call */ @@ -387,29 +404,43 @@ static int pcap_read_nocb_remote(pcap_t *p, struct pcap_pkthdr *pkt_header, u_ch * 'timeout', in pcap_t, is in milliseconds; we have to convert it into sec and microsec */ tv.tv_sec = p->opt.timeout / 1000; - tv.tv_usec = (p->opt.timeout - tv.tv_sec * 1000) * 1000; + tv.tv_usec = (suseconds_t)((p->opt.timeout - tv.tv_sec * 1000) * 1000); - /* Watch out sockdata to see if it has input */ - FD_ZERO(&rfds); - - /* - * 'fp->rmt_sockdata' has always to be set before calling the select(), - * since it is cleared by the select() - */ - FD_SET(pr->rmt_sockdata, &rfds); - - retval = select((int) pr->rmt_sockdata + 1, &rfds, NULL, NULL, &tv); - if (retval == -1) - { -#ifndef _WIN32 - if (errno == EINTR) - { - /* Interrupted. */ - return 0; - } +#ifdef HAVE_OPENSSL + /* Check if we still have bytes available in the last decoded TLS record. + * If that's the case, we know SSL_read will not block. */ + retval = pr->data_ssl && SSL_pending(pr->data_ssl) > 0; #endif - sock_geterror("select()", p->errbuf, PCAP_ERRBUF_SIZE); - return -1; + if (! retval) + { + /* Watch out sockdata to see if it has input */ + FD_ZERO(&rfds); + + /* + * 'fp->rmt_sockdata' has always to be set before calling the select(), + * since it is cleared by the select() + */ + FD_SET(pr->rmt_sockdata, &rfds); + +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + retval = 1; +#else + retval = select((int) pr->rmt_sockdata + 1, &rfds, NULL, NULL, &tv); +#endif + + if (retval == -1) + { +#ifndef _WIN32 + if (errno == EINTR) + { + /* Interrupted. */ + return 0; + } +#endif + sock_geterrmsg(p->errbuf, PCAP_ERRBUF_SIZE, + "select() failed"); + return -1; + } } /* There is no data waiting, so return '0' */ @@ -427,7 +458,7 @@ static int pcap_read_nocb_remote(pcap_t *p, struct pcap_pkthdr *pkt_header, u_ch if (pr->rmt_flags & PCAP_OPENFLAG_DATATX_UDP) { /* Read the entire message from the network */ - msglen = sock_recv_dgram(pr->rmt_sockdata, p->buffer, + msglen = sock_recv_dgram(pr->rmt_sockdata, pr->data_ssl, p->buffer, p->bufsize, p->errbuf, PCAP_ERRBUF_SIZE); if (msglen == -1) { @@ -444,7 +475,7 @@ static int pcap_read_nocb_remote(pcap_t *p, struct pcap_pkthdr *pkt_header, u_ch /* * Message is shorter than an rpcap header. */ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "UDP packet message is shorter than an rpcap header"); return -1; } @@ -455,7 +486,7 @@ static int pcap_read_nocb_remote(pcap_t *p, struct pcap_pkthdr *pkt_header, u_ch * Message is shorter than the header claims it * is. */ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "UDP packet message is shorter than its rpcap header claims"); return -1; } @@ -471,8 +502,7 @@ static int pcap_read_nocb_remote(pcap_t *p, struct pcap_pkthdr *pkt_header, u_ch * The size we should get is the size of the * packet header. */ - status = rpcap_read_packet_msg(pr->rmt_sockdata, p, - sizeof(struct rpcap_header)); + status = rpcap_read_packet_msg(pr, p, sizeof(struct rpcap_header)); if (status == -1) { /* Network error. */ @@ -500,12 +530,11 @@ static int pcap_read_nocb_remote(pcap_t *p, struct pcap_pkthdr *pkt_header, u_ch * subtracting in order to avoid an * overflow.) */ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Server sent us a message larger than the largest expected packet message"); return -1; } - status = rpcap_read_packet_msg(pr->rmt_sockdata, p, - sizeof(struct rpcap_header) + plen); + status = rpcap_read_packet_msg(pr, p, sizeof(struct rpcap_header) + plen); if (status == -1) { /* Network error. */ @@ -534,7 +563,7 @@ static int pcap_read_nocb_remote(pcap_t *p, struct pcap_pkthdr *pkt_header, u_ch /* * Did the server specify the version we negotiated? */ - if (rpcap_check_msg_ver(pr->rmt_sockdata, pr->protocol_version, + if (rpcap_check_msg_ver(pr->rmt_sockdata, pr->data_ssl, pr->protocol_version, header, p->errbuf) == -1) { return 0; /* Return 'no packets received' */ @@ -550,7 +579,7 @@ static int pcap_read_nocb_remote(pcap_t *p, struct pcap_pkthdr *pkt_header, u_ch if (ntohl(net_pkt_header->caplen) > plen) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Packet's captured data goes past the end of the received packet message."); return -1; } @@ -621,6 +650,21 @@ static int pcap_read_rpcap(pcap_t *p, int cnt, pcap_handler callback, u_char *us } } + /* + * 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(cnt)) + cnt = INT_MAX; + while (n < cnt || PACKET_COUNT_IS_UNLIMITED(cnt)) { /* @@ -643,9 +687,14 @@ static int pcap_read_rpcap(pcap_t *p, int cnt, pcap_handler callback, u_char *us if (ret == 1) { /* - * We got a packet. Hand it to the callback - * and count it so we can return the count. + * We got a packet. + * + * Do whatever post-processing is necessary, hand + * it to the callback, and count it so we can + * return the count. */ + pcap_post_process(p->linktype, pr->byte_swapped, + &pkt_header, pkt_data); (*callback)(user, &pkt_header, pkt_data); n++; } @@ -678,7 +727,9 @@ static int pcap_read_rpcap(pcap_t *p, int cnt, pcap_handler callback, u_char *us } /* - * This function sends a CLOSE command to the capture server. + * This function sends a CLOSE command to the capture server if we're in + * passive mode and an ENDCAP command to the capture server if we're in + * active mode. * * It is called when the user calls pcap_close(). It sends a command * to our peer that says 'ok, let's stop capturing'. @@ -714,7 +765,7 @@ static void pcap_cleanup_rpcap(pcap_t *fp) * we're closing this pcap_t, and have no place to report * the error. No reply is sent to this message. */ - (void)sock_send(pr->rmt_sockctrl, (char *)&header, + (void)sock_send(pr->rmt_sockctrl, pr->ctrl_ssl, (char *)&header, sizeof(struct rpcap_header), NULL, 0); } else @@ -727,7 +778,7 @@ static void pcap_cleanup_rpcap(pcap_t *fp) * as we're closing this pcap_t, and have no place to * report the error. */ - if (sock_send(pr->rmt_sockctrl, (char *)&header, + if (sock_send(pr->rmt_sockctrl, pr->ctrl_ssl, (char *)&header, sizeof(struct rpcap_header), NULL, 0) == 0) { /* @@ -735,11 +786,11 @@ static void pcap_cleanup_rpcap(pcap_t *fp) * as we're closing this pcap_t, and have no * place to report the error. */ - if (rpcap_process_msg_header(pr->rmt_sockctrl, + if (rpcap_process_msg_header(pr->rmt_sockctrl, pr->ctrl_ssl, pr->protocol_version, RPCAP_MSG_ENDCAP_REQ, &header, NULL) == 0) { - (void)rpcap_discard(pr->rmt_sockctrl, + (void)rpcap_discard(pr->rmt_sockctrl, pr->ctrl_ssl, header.plen, NULL); } } @@ -747,14 +798,35 @@ static void pcap_cleanup_rpcap(pcap_t *fp) if (pr->rmt_sockdata) { +#ifdef HAVE_OPENSSL + if (pr->data_ssl) + { + // Finish using the SSL handle for the data socket. + // This must be done *before* the socket is closed. + ssl_finish(pr->data_ssl); + pr->data_ssl = NULL; + } +#endif sock_close(pr->rmt_sockdata, NULL, 0); pr->rmt_sockdata = 0; } if ((!active) && (pr->rmt_sockctrl)) + { +#ifdef HAVE_OPENSSL + if (pr->ctrl_ssl) + { + // Finish using the SSL handle for the control socket. + // This must be done *before* the socket is closed. + ssl_finish(pr->ctrl_ssl); + pr->ctrl_ssl = NULL; + } +#endif sock_close(pr->rmt_sockctrl, NULL, 0); + } pr->rmt_sockctrl = 0; + pr->ctrl_ssl = NULL; if (pr->currentfilter) { @@ -848,7 +920,7 @@ static struct pcap_stat *rpcap_stats_rpcap(pcap_t *p, struct pcap_stat *ps, int if (mode != PCAP_STATS_STANDARD) #endif { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Invalid stats mode %d", mode); return NULL; } @@ -879,19 +951,19 @@ static struct pcap_stat *rpcap_stats_rpcap(pcap_t *p, struct pcap_stat *ps, int RPCAP_MSG_STATS_REQ, 0, 0); /* Send the PCAP_STATS command */ - if (sock_send(pr->rmt_sockctrl, (char *)&header, + if (sock_send(pr->rmt_sockctrl, pr->ctrl_ssl, (char *)&header, sizeof(struct rpcap_header), p->errbuf, PCAP_ERRBUF_SIZE) < 0) return NULL; /* Unrecoverable network error */ /* Receive and process the reply message header. */ - if (rpcap_process_msg_header(pr->rmt_sockctrl, pr->protocol_version, + if (rpcap_process_msg_header(pr->rmt_sockctrl, pr->ctrl_ssl, pr->protocol_version, RPCAP_MSG_STATS_REQ, &header, p->errbuf) == -1) return NULL; /* Error */ plen = header.plen; /* Read the reply body */ - if (rpcap_recv(pr->rmt_sockctrl, (char *)&netstats, + if (rpcap_recv(pr->rmt_sockctrl, pr->ctrl_ssl, (char *)&netstats, sizeof(struct rpcap_stats), &plen, p->errbuf) == -1) goto error; @@ -908,7 +980,7 @@ static struct pcap_stat *rpcap_stats_rpcap(pcap_t *p, struct pcap_stat *ps, int #endif /* _WIN32 */ /* Discard the rest of the message. */ - if (rpcap_discard(pr->rmt_sockctrl, plen, p->errbuf) == -1) + if (rpcap_discard(pr->rmt_sockctrl, pr->ctrl_ssl, plen, p->errbuf) == -1) goto error_nodiscard; return ps; @@ -919,7 +991,7 @@ error: * We already reported an error; if this gets an error, just * drive on. */ - (void)rpcap_discard(pr->rmt_sockctrl, plen, NULL); + (void)rpcap_discard(pr->rmt_sockctrl, pr->ctrl_ssl, plen, NULL); error_nodiscard: return NULL; @@ -957,11 +1029,10 @@ rpcap_remoteact_getsock(const char *host, int *error, char *errbuf) hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; - retval = getaddrinfo(host, "0", &hints, &addrinfo); + retval = sock_initaddress(host, NULL, &hints, &addrinfo, errbuf, + PCAP_ERRBUF_SIZE); if (retval != 0) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "getaddrinfo() %s", - gai_strerror(retval)); *error = 1; return NULL; } @@ -1014,7 +1085,7 @@ static int pcap_startcapture_remote(pcap_t *fp) struct pcap_rpcap *pr = fp->priv; /* structure used when doing a remote live capture */ char sendbuf[RPCAP_NETBUF_SIZE]; /* temporary buffer in which data to be sent is buffered */ int sendbufidx = 0; /* index which keeps the number of bytes currently buffered */ - char portdata[PCAP_BUF_SIZE]; /* temp variable needed to keep the network port for the data connection */ + uint16 portdata = 0; /* temp variable needed to keep the network port for the data connection */ uint32 plen; int active = 0; /* '1' if we're in active mode */ struct activehosts *temp; /* temp var needed to scan the host list chain, to detect if we're in active mode */ @@ -1027,6 +1098,8 @@ static int pcap_startcapture_remote(pcap_t *fp) struct sockaddr_storage saddr; /* temp, needed to retrieve the network data port chosen on the local machine */ socklen_t saddrlen; /* temp, needed to retrieve the network data port chosen on the local machine */ int ai_family; /* temp, keeps the address family used by the control connection */ + struct sockaddr_in *sin4; + struct sockaddr_in6 *sin6; /* RPCAP-related variables*/ struct rpcap_header header; /* header of the RPCAP packet */ @@ -1039,6 +1112,12 @@ static int pcap_startcapture_remote(pcap_t *fp) int sockbufsize = 0; uint32 server_sockbufsize; + // Take the opportunity to clear pr->data_ssl before any goto error, + // as it seems p->priv is not zeroed after its malloced. + // XXX - it now should be, as it's allocated by pcap_alloc_pcap_t(), + // which does a calloc(). + pr->data_ssl = NULL; + /* * Let's check if sampling has been required. * If so, let's set it first @@ -1070,7 +1149,8 @@ static int pcap_startcapture_remote(pcap_t *fp) saddrlen = sizeof(struct sockaddr_storage); if (getpeername(pr->rmt_sockctrl, (struct sockaddr *) &saddr, &saddrlen) == -1) { - sock_geterror("getsockname()", fp->errbuf, PCAP_ERRBUF_SIZE); + sock_geterrmsg(fp->errbuf, PCAP_ERRBUF_SIZE, + "getsockname() failed"); goto error_nodiscard; } ai_family = ((struct sockaddr_storage *) &saddr)->ss_family; @@ -1079,7 +1159,8 @@ static int pcap_startcapture_remote(pcap_t *fp) if (getnameinfo((struct sockaddr *) &saddr, saddrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST)) { - sock_geterror("getnameinfo()", fp->errbuf, PCAP_ERRBUF_SIZE); + sock_geterrmsg(fp->errbuf, PCAP_ERRBUF_SIZE, + "getnameinfo() failed"); goto error_nodiscard; } @@ -1102,10 +1183,10 @@ static int pcap_startcapture_remote(pcap_t *fp) hints.ai_flags = AI_PASSIVE; /* Data connection is opened by the server toward the client */ /* Let's the server pick up a free network port for us */ - if (sock_initaddress(NULL, "0", &hints, &addrinfo, fp->errbuf, PCAP_ERRBUF_SIZE) == -1) + if (sock_initaddress(NULL, NULL, &hints, &addrinfo, fp->errbuf, PCAP_ERRBUF_SIZE) == -1) goto error_nodiscard; - if ((sockdata = sock_open(addrinfo, SOCKOPEN_SERVER, + if ((sockdata = sock_open(NULL, addrinfo, SOCKOPEN_SERVER, 1 /* max 1 connection in queue */, fp->errbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET) goto error_nodiscard; @@ -1117,15 +1198,27 @@ static int pcap_startcapture_remote(pcap_t *fp) saddrlen = sizeof(struct sockaddr_storage); if (getsockname(sockdata, (struct sockaddr *) &saddr, &saddrlen) == -1) { - sock_geterror("getsockname()", fp->errbuf, PCAP_ERRBUF_SIZE); + sock_geterrmsg(fp->errbuf, PCAP_ERRBUF_SIZE, + "getsockname() failed"); goto error_nodiscard; } - /* Get the local port the system picked up */ - if (getnameinfo((struct sockaddr *) &saddr, saddrlen, NULL, - 0, portdata, sizeof(portdata), NI_NUMERICSERV)) - { - sock_geterror("getnameinfo()", fp->errbuf, PCAP_ERRBUF_SIZE); + switch (saddr.ss_family) { + + case AF_INET: + sin4 = (struct sockaddr_in *)&saddr; + portdata = sin4->sin_port; + break; + + case AF_INET6: + sin6 = (struct sockaddr_in6 *)&saddr; + portdata = sin6->sin6_port; + break; + + default: + snprintf(fp->errbuf, PCAP_ERRBUF_SIZE, + "Local address has unknown address family %u", + saddr.ss_family); goto error_nodiscard; } } @@ -1158,8 +1251,7 @@ static int pcap_startcapture_remote(pcap_t *fp) /* portdata on the openreq is meaningful only if we're in active mode */ if ((active) || (pr->rmt_flags & PCAP_OPENFLAG_DATATX_UDP)) { - sscanf(portdata, "%d", (int *)&(startcapreq->portdata)); /* cast to avoid a compiler warning */ - startcapreq->portdata = htons(startcapreq->portdata); + startcapreq->portdata = portdata; } startcapreq->snaplen = htonl(fp->snapshot); @@ -1178,18 +1270,18 @@ static int pcap_startcapture_remote(pcap_t *fp) if (pcap_pack_bpffilter(fp, &sendbuf[sendbufidx], &sendbufidx, &fp->fcode)) goto error_nodiscard; - if (sock_send(pr->rmt_sockctrl, sendbuf, sendbufidx, fp->errbuf, + if (sock_send(pr->rmt_sockctrl, pr->ctrl_ssl, sendbuf, sendbufidx, fp->errbuf, PCAP_ERRBUF_SIZE) < 0) goto error_nodiscard; /* Receive and process the reply message header. */ - if (rpcap_process_msg_header(pr->rmt_sockctrl, pr->protocol_version, + if (rpcap_process_msg_header(pr->rmt_sockctrl, pr->ctrl_ssl, pr->protocol_version, RPCAP_MSG_STARTCAP_REQ, &header, fp->errbuf) == -1) goto error_nodiscard; plen = header.plen; - if (rpcap_recv(pr->rmt_sockctrl, (char *)&startcapreply, + if (rpcap_recv(pr->rmt_sockctrl, pr->ctrl_ssl, (char *)&startcapreply, sizeof(struct rpcap_startcapreply), &plen, fp->errbuf) == -1) goto error; @@ -1208,16 +1300,18 @@ static int pcap_startcapture_remote(pcap_t *fp) { if (!active) { + char portstring[PCAP_BUF_SIZE]; + memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = ai_family; /* Use the same address family of the control socket */ hints.ai_socktype = (pr->rmt_flags & PCAP_OPENFLAG_DATATX_UDP) ? SOCK_DGRAM : SOCK_STREAM; - pcap_snprintf(portdata, PCAP_BUF_SIZE, "%d", ntohs(startcapreply.portdata)); + snprintf(portstring, PCAP_BUF_SIZE, "%d", ntohs(startcapreply.portdata)); /* Let's the server pick up a free network port for us */ - if (sock_initaddress(host, portdata, &hints, &addrinfo, fp->errbuf, PCAP_ERRBUF_SIZE) == -1) + if (sock_initaddress(host, portstring, &hints, &addrinfo, fp->errbuf, PCAP_ERRBUF_SIZE) == -1) goto error; - if ((sockdata = sock_open(addrinfo, SOCKOPEN_CLIENT, 0, fp->errbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET) + if ((sockdata = sock_open(host, addrinfo, SOCKOPEN_CLIENT, 0, fp->errbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET) goto error; /* addrinfo is no longer used */ @@ -1235,7 +1329,8 @@ static int pcap_startcapture_remote(pcap_t *fp) if (socktemp == INVALID_SOCKET) { - sock_geterror("accept()", fp->errbuf, PCAP_ERRBUF_SIZE); + sock_geterrmsg(fp->errbuf, PCAP_ERRBUF_SIZE, + "accept() failed"); goto error; } @@ -1248,6 +1343,14 @@ static int pcap_startcapture_remote(pcap_t *fp) /* Let's save the socket of the data connection */ pr->rmt_sockdata = sockdata; +#ifdef HAVE_OPENSSL + if (pr->uses_ssl) + { + pr->data_ssl = ssl_promotion(0, sockdata, fp->errbuf, PCAP_ERRBUF_SIZE); + if (! pr->data_ssl) goto error; + } +#endif + /* * Set the size of the socket buffer for the data socket. * It has the same size as the local capture buffer used @@ -1261,7 +1364,8 @@ static int pcap_startcapture_remote(pcap_t *fp) res = getsockopt(sockdata, SOL_SOCKET, SO_RCVBUF, (char *)&sockbufsize, &itemp); if (res == -1) { - sock_geterror("pcap_startcapture_remote(): getsockopt() failed", fp->errbuf, PCAP_ERRBUF_SIZE); + sock_geterrmsg(fp->errbuf, PCAP_ERRBUF_SIZE, + "pcap_startcapture_remote(): getsockopt() failed"); goto error; } @@ -1335,7 +1439,7 @@ static int pcap_startcapture_remote(pcap_t *fp) fp->cc = 0; /* Discard the rest of the message. */ - if (rpcap_discard(pr->rmt_sockctrl, plen, fp->errbuf) == -1) + if (rpcap_discard(pr->rmt_sockctrl, pr->ctrl_ssl, plen, fp->errbuf) == -1) goto error_nodiscard; /* @@ -1375,14 +1479,36 @@ error: * We already reported an error; if this gets an error, just * drive on. */ - (void)rpcap_discard(pr->rmt_sockctrl, plen, NULL); + (void)rpcap_discard(pr->rmt_sockctrl, pr->ctrl_ssl, plen, NULL); error_nodiscard: - if ((sockdata) && (sockdata != -1)) /* we can be here because sockdata said 'error' */ +#ifdef HAVE_OPENSSL + if (pr->data_ssl) + { + // Finish using the SSL handle for the data socket. + // This must be done *before* the socket is closed. + ssl_finish(pr->data_ssl); + pr->data_ssl = NULL; + } +#endif + + /* we can be here because sockdata said 'error' */ + if ((sockdata != 0) && (sockdata != INVALID_SOCKET)) sock_close(sockdata, NULL, 0); if (!active) + { +#ifdef HAVE_OPENSSL + if (pr->ctrl_ssl) + { + // Finish using the SSL handle for the control socket. + // This must be done *before* the socket is closed. + ssl_finish(pr->ctrl_ssl); + pr->ctrl_ssl = NULL; + } +#endif sock_close(pr->rmt_sockctrl, NULL, 0); + } if (addrinfo != NULL) freeaddrinfo(addrinfo); @@ -1408,7 +1534,7 @@ error_nodiscard: * This function can be called in two cases: * - pcap_startcapture_remote() is called (we have to send the filter * along with the 'start capture' command) - * - we want to udpate the filter during a capture (i.e. pcap_setfilter() + * - we want to update the filter during a capture (i.e. pcap_setfilter() * after the capture has been started) * * This function serializes the filter into the sending buffer ('sendbuf', @@ -1515,19 +1641,19 @@ static int pcap_updatefilter_remote(pcap_t *fp, struct bpf_program *prog) if (pcap_pack_bpffilter(fp, &sendbuf[sendbufidx], &sendbufidx, prog)) return -1; - if (sock_send(pr->rmt_sockctrl, sendbuf, sendbufidx, fp->errbuf, + if (sock_send(pr->rmt_sockctrl, pr->ctrl_ssl, sendbuf, sendbufidx, fp->errbuf, PCAP_ERRBUF_SIZE) < 0) return -1; /* Receive and process the reply message header. */ - if (rpcap_process_msg_header(pr->rmt_sockctrl, pr->protocol_version, + if (rpcap_process_msg_header(pr->rmt_sockctrl, pr->ctrl_ssl, pr->protocol_version, RPCAP_MSG_UPDATEFILTER_REQ, &header, fp->errbuf) == -1) return -1; /* * It shouldn't have any contents; discard it if it does. */ - if (rpcap_discard(pr->rmt_sockctrl, header.plen, fp->errbuf) == -1) + if (rpcap_discard(pr->rmt_sockctrl, pr->ctrl_ssl, header.plen, fp->errbuf) == -1) return -1; return 0; @@ -1618,14 +1744,16 @@ static int pcap_createfilter_norpcappkt(pcap_t *fp, struct bpf_program *prog) saddrlen = sizeof(struct sockaddr_storage); if (getpeername(pr->rmt_sockctrl, (struct sockaddr *) &saddr, &saddrlen) == -1) { - sock_geterror("getpeername()", fp->errbuf, PCAP_ERRBUF_SIZE); + sock_geterrmsg(fp->errbuf, PCAP_ERRBUF_SIZE, + "getpeername() failed"); return -1; } if (getnameinfo((struct sockaddr *) &saddr, saddrlen, peeraddress, sizeof(peeraddress), peerctrlport, sizeof(peerctrlport), NI_NUMERICHOST | NI_NUMERICSERV)) { - sock_geterror("getnameinfo()", fp->errbuf, PCAP_ERRBUF_SIZE); + sock_geterrmsg(fp->errbuf, PCAP_ERRBUF_SIZE, + "getnameinfo() failed"); return -1; } @@ -1633,7 +1761,8 @@ static int pcap_createfilter_norpcappkt(pcap_t *fp, struct bpf_program *prog) /* Get the name/port of the current host */ if (getsockname(pr->rmt_sockctrl, (struct sockaddr *) &saddr, &saddrlen) == -1) { - sock_geterror("getsockname()", fp->errbuf, PCAP_ERRBUF_SIZE); + sock_geterrmsg(fp->errbuf, PCAP_ERRBUF_SIZE, + "getsockname() failed"); return -1; } @@ -1641,21 +1770,24 @@ static int pcap_createfilter_norpcappkt(pcap_t *fp, struct bpf_program *prog) if (getnameinfo((struct sockaddr *) &saddr, saddrlen, myaddress, sizeof(myaddress), myctrlport, sizeof(myctrlport), NI_NUMERICHOST | NI_NUMERICSERV)) { - sock_geterror("getnameinfo()", fp->errbuf, PCAP_ERRBUF_SIZE); + sock_geterrmsg(fp->errbuf, PCAP_ERRBUF_SIZE, + "getnameinfo() failed"); return -1; } /* Let's now check the data port */ if (getsockname(pr->rmt_sockdata, (struct sockaddr *) &saddr, &saddrlen) == -1) { - sock_geterror("getsockname()", fp->errbuf, PCAP_ERRBUF_SIZE); + sock_geterrmsg(fp->errbuf, PCAP_ERRBUF_SIZE, + "getsockname() failed"); return -1; } /* Get the local port the system picked up */ if (getnameinfo((struct sockaddr *) &saddr, saddrlen, NULL, 0, mydataport, sizeof(mydataport), NI_NUMERICSERV)) { - sock_geterror("getnameinfo()", fp->errbuf, PCAP_ERRBUF_SIZE); + sock_geterrmsg(fp->errbuf, PCAP_ERRBUF_SIZE, + "getnameinfo() failed"); return -1; } @@ -1672,7 +1804,7 @@ static int pcap_createfilter_norpcappkt(pcap_t *fp, struct bpf_program *prog) mydataport) == -1) { /* Failed. */ - pcap_snprintf(fp->errbuf, PCAP_ERRBUF_SIZE, + snprintf(fp->errbuf, PCAP_ERRBUF_SIZE, "Can't allocate memory for new filter"); return -1; } @@ -1689,7 +1821,7 @@ static int pcap_createfilter_norpcappkt(pcap_t *fp, struct bpf_program *prog) myaddress, peeraddress, mydataport) == -1) { /* Failed. */ - pcap_snprintf(fp->errbuf, PCAP_ERRBUF_SIZE, + snprintf(fp->errbuf, PCAP_ERRBUF_SIZE, "Can't allocate memory for new filter"); return -1; } @@ -1745,12 +1877,12 @@ static int pcap_setsampling_remote(pcap_t *fp) * that do fit into the message. */ if (fp->rmt_samp.method < 0 || fp->rmt_samp.method > 255) { - pcap_snprintf(fp->errbuf, PCAP_ERRBUF_SIZE, + snprintf(fp->errbuf, PCAP_ERRBUF_SIZE, "Invalid sampling method %d", fp->rmt_samp.method); return -1; } if (fp->rmt_samp.value < 0 || fp->rmt_samp.value > 65535) { - pcap_snprintf(fp->errbuf, PCAP_ERRBUF_SIZE, + snprintf(fp->errbuf, PCAP_ERRBUF_SIZE, "Invalid sampling value %d", fp->rmt_samp.value); return -1; } @@ -1775,19 +1907,19 @@ static int pcap_setsampling_remote(pcap_t *fp) sampling_pars->method = (uint8)fp->rmt_samp.method; sampling_pars->value = (uint16)htonl(fp->rmt_samp.value); - if (sock_send(pr->rmt_sockctrl, sendbuf, sendbufidx, fp->errbuf, + if (sock_send(pr->rmt_sockctrl, pr->ctrl_ssl, sendbuf, sendbufidx, fp->errbuf, PCAP_ERRBUF_SIZE) < 0) return -1; /* Receive and process the reply message header. */ - if (rpcap_process_msg_header(pr->rmt_sockctrl, pr->protocol_version, + if (rpcap_process_msg_header(pr->rmt_sockctrl, pr->ctrl_ssl, pr->protocol_version, RPCAP_MSG_SETSAMPLING_REQ, &header, fp->errbuf) == -1) return -1; /* * It shouldn't have any contents; discard it if it does. */ - if (rpcap_discard(pr->rmt_sockctrl, header.plen, fp->errbuf) == -1) + if (rpcap_discard(pr->rmt_sockctrl, pr->ctrl_ssl, header.plen, fp->errbuf) == -1) return -1; return 0; @@ -1821,6 +1953,10 @@ static int pcap_setsampling_remote(pcap_t *fp) * \param ver: pointer to variable to which to set the protocol version * number we selected. * + * \param byte_swapped: pointer to variable to which to set 1 if the + * byte order the server says it has is byte-swapped from ours, 0 + * otherwise (whether it's the same as ours or is unknown). + * * \param auth: authentication parameters that have to be sent. * * \param errbuf: a pointer to a user-allocated buffer (of size @@ -1831,7 +1967,8 @@ static int pcap_setsampling_remote(pcap_t *fp) * \return '0' if everything is fine, '-1' for an error. For errors, * an error message string is returned in the 'errbuf' variable. */ -static int rpcap_doauth(SOCKET sockctrl, uint8 *ver, struct pcap_rmtauth *auth, char *errbuf) +static int rpcap_doauth(SOCKET sockctrl, SSL *ssl, uint8 *ver, + int *byte_swapped, struct pcap_rmtauth *auth, char *errbuf) { char sendbuf[RPCAP_NETBUF_SIZE]; /* temporary buffer in which data that has to be sent is buffered */ int sendbufidx = 0; /* index which keeps the number of bytes currently buffered */ @@ -1843,6 +1980,8 @@ static int rpcap_doauth(SOCKET sockctrl, uint8 *ver, struct pcap_rmtauth *auth, uint32 plen; struct rpcap_authreply authreply; /* authentication reply message */ uint8 ourvers; + int has_byte_order; /* The server sent its version of the byte-order magic number */ + u_int their_byte_order_magic; /* Here's what it is */ if (auth) { @@ -1859,7 +1998,7 @@ static int rpcap_doauth(SOCKET sockctrl, uint8 *ver, struct pcap_rmtauth *auth, str_length = strlen(auth->username); if (str_length > 65535) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "User name is too long (> 65535 bytes)"); + snprintf(errbuf, PCAP_ERRBUF_SIZE, "User name is too long (> 65535 bytes)"); return -1; } length += (uint16)str_length; @@ -1869,7 +2008,7 @@ static int rpcap_doauth(SOCKET sockctrl, uint8 *ver, struct pcap_rmtauth *auth, str_length = strlen(auth->password); if (str_length > 65535) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Password is too long (> 65535 bytes)"); + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Password is too long (> 65535 bytes)"); return -1; } length += (uint16)str_length; @@ -1877,7 +2016,7 @@ static int rpcap_doauth(SOCKET sockctrl, uint8 *ver, struct pcap_rmtauth *auth, break; default: - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Authentication type not recognized."); + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Authentication type not recognized."); return -1; } @@ -1930,12 +2069,12 @@ static int rpcap_doauth(SOCKET sockctrl, uint8 *ver, struct pcap_rmtauth *auth, rpauth->slen2 = htons(rpauth->slen2); } - if (sock_send(sockctrl, sendbuf, sendbufidx, errbuf, + if (sock_send(sockctrl, ssl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) < 0) return -1; /* Receive and process the reply message header */ - if (rpcap_process_msg_header(sockctrl, 0, RPCAP_MSG_AUTH_REQ, + if (rpcap_process_msg_header(sockctrl, ssl, 0, RPCAP_MSG_AUTH_REQ, &header, errbuf) == -1) return -1; @@ -1947,24 +2086,53 @@ static int rpcap_doauth(SOCKET sockctrl, uint8 *ver, struct pcap_rmtauth *auth, plen = header.plen; if (plen != 0) { - /* Yes - is it big enough to be version information? */ - if (plen < sizeof(struct rpcap_authreply)) + size_t reply_len; + + /* Yes - is it big enough to include version information? */ + if (plen < sizeof(struct rpcap_authreply_old)) { /* No - discard it and fail. */ - (void)rpcap_discard(sockctrl, plen, NULL); + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "Authenticaton reply from server is too short"); + (void)rpcap_discard(sockctrl, ssl, plen, NULL); + return -1; + } + + /* Yes - does it include server byte order information? */ + if (plen == sizeof(struct rpcap_authreply_old)) + { + /* No - just read the version information */ + has_byte_order = 0; + reply_len = sizeof(struct rpcap_authreply_old); + } + else if (plen >= sizeof(struct rpcap_authreply_old)) + { + /* Yes - read it all. */ + has_byte_order = 1; + reply_len = sizeof(struct rpcap_authreply); + } + else + { + /* + * Too long for old reply, too short for new reply. + * Discard it and fail. + */ + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "Authenticaton reply from server is too short"); + (void)rpcap_discard(sockctrl, ssl, plen, NULL); return -1; } /* Read the reply body */ - if (rpcap_recv(sockctrl, (char *)&authreply, - sizeof(struct rpcap_authreply), &plen, errbuf) == -1) + if (rpcap_recv(sockctrl, ssl, (char *)&authreply, + reply_len, &plen, errbuf) == -1) { - (void)rpcap_discard(sockctrl, plen, NULL); + (void)rpcap_discard(sockctrl, ssl, plen, NULL); return -1; } /* Discard the rest of the message, if there is any. */ - if (rpcap_discard(sockctrl, plen, errbuf) == -1) + if (rpcap_discard(sockctrl, ssl, plen, errbuf) == -1) return -1; /* @@ -1976,16 +2144,36 @@ static int rpcap_doauth(SOCKET sockctrl, uint8 *ver, struct pcap_rmtauth *auth, /* * Bogus - give up on this server. */ - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "The server's minimum supported protocol version is greater than its maximum supported protocol version"); return -1; } + + if (has_byte_order) + { + their_byte_order_magic = authreply.byte_order_magic; + } + else + { + /* + * The server didn't tell us what its byte + * order is; assume it's ours. + */ + their_byte_order_magic = RPCAP_BYTE_ORDER_MAGIC; + } } else { /* No - it supports only version 0. */ authreply.minvers = 0; authreply.maxvers = 0; + + /* + * And it didn't tell us what its byte order is; assume + * it's ours. + */ + has_byte_order = 0; + their_byte_order_magic = RPCAP_BYTE_ORDER_MAGIC; } /* @@ -2018,6 +2206,27 @@ static int rpcap_doauth(SOCKET sockctrl, uint8 *ver, struct pcap_rmtauth *auth, goto novers; } + /* + * Is the server byte order the opposite of ours? + */ + if (their_byte_order_magic == RPCAP_BYTE_ORDER_MAGIC) + { + /* No, it's the same. */ + *byte_swapped = 0; + } + else if (their_byte_order_magic == RPCAP_BYTE_ORDER_MAGIC_SWAPPED) + { + /* Yes, it's the opposite of ours. */ + *byte_swapped = 1; + } + else + { + /* They sent us something bogus. */ + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "The server did not send us a valid byte order value"); + return -1; + } + *ver = ourvers; return 0; @@ -2025,7 +2234,7 @@ novers: /* * There is no version we both support; that is a fatal error. */ - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "The server doesn't support any protocol version that we support"); return -1; } @@ -2034,7 +2243,7 @@ novers: static int pcap_getnonblock_rpcap(pcap_t *p) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Non-blocking mode isn't supported for capturing remotely with rpcap"); return (-1); } @@ -2042,14 +2251,15 @@ pcap_getnonblock_rpcap(pcap_t *p) static int pcap_setnonblock_rpcap(pcap_t *p, int nonblock _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Non-blocking mode isn't supported for capturing remotely with rpcap"); return (-1); } static int rpcap_setup_session(const char *source, struct pcap_rmtauth *auth, - int *activep, SOCKET *sockctrlp, uint8 *protocol_versionp, + int *activep, SOCKET *sockctrlp, uint8 *uses_sslp, SSL **sslp, + int rmt_flags, uint8 *protocol_versionp, int *byte_swappedp, char *host, char *port, char *iface, char *errbuf) { int type; @@ -2061,7 +2271,8 @@ rpcap_setup_session(const char *source, struct pcap_rmtauth *auth, * You must have a valid source string even if we're in active mode, * because otherwise the call to the following function will fail. */ - if (pcap_parsesrcstr(source, &type, host, port, iface, errbuf) == -1) + if (pcap_parsesrcstr_ex(source, &type, host, port, iface, uses_sslp, + errbuf) == -1) return -1; /* @@ -2069,13 +2280,25 @@ rpcap_setup_session(const char *source, struct pcap_rmtauth *auth, */ if (type != PCAP_SRC_IFREMOTE) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Non-remote interface passed to remote capture routine"); return -1; } + /* + * We don't yet support DTLS, so if the user asks for a TLS + * connection and asks for data packets to be sent over UDP, + * we have to give up. + */ + if (*uses_sslp && (rmt_flags & PCAP_OPENFLAG_DATATX_UDP)) + { + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "TLS not supported with UDP forward of remote packets"); + return -1; + } + /* Warning: this call can be the first one called by the user. */ - /* For this reason, we have to initialize the WinSock support. */ + /* For this reason, we have to initialize the Winsock support. */ if (sock_init(errbuf, PCAP_ERRBUF_SIZE) == -1) return -1; @@ -2085,7 +2308,9 @@ rpcap_setup_session(const char *source, struct pcap_rmtauth *auth, { *activep = 1; *sockctrlp = activeconn->sockctrl; + *sslp = activeconn->ssl; *protocol_versionp = activeconn->protocol_version; + *byte_swappedp = activeconn->byte_swapped; } else { @@ -2123,7 +2348,7 @@ rpcap_setup_session(const char *source, struct pcap_rmtauth *auth, return -1; } - if ((*sockctrlp = sock_open(addrinfo, SOCKOPEN_CLIENT, 0, + if ((*sockctrlp = sock_open(host, addrinfo, SOCKOPEN_CLIENT, 0, errbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET) { freeaddrinfo(addrinfo); @@ -2134,9 +2359,36 @@ rpcap_setup_session(const char *source, struct pcap_rmtauth *auth, freeaddrinfo(addrinfo); addrinfo = NULL; - if (rpcap_doauth(*sockctrlp, protocol_versionp, auth, - errbuf) == -1) + if (*uses_sslp) { +#ifdef HAVE_OPENSSL + *sslp = ssl_promotion(0, *sockctrlp, errbuf, + PCAP_ERRBUF_SIZE); + if (!*sslp) + { + sock_close(*sockctrlp, NULL, 0); + return -1; + } +#else + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "No TLS support"); + sock_close(*sockctrlp, NULL, 0); + return -1; +#endif + } + + if (rpcap_doauth(*sockctrlp, *sslp, protocol_versionp, + byte_swappedp, auth, errbuf) == -1) + { +#ifdef HAVE_OPENSSL + if (*sslp) + { + // Finish using the SSL handle for the socket. + // This must be done *before* the socket is + // closed. + ssl_finish(*sslp); + } +#endif sock_close(*sockctrlp, NULL, 0); return -1; } @@ -2190,7 +2442,9 @@ pcap_t *pcap_open_rpcap(const char *source, int snaplen, int flags, int read_tim struct pcap_rpcap *pr; /* structure used when doing a remote live capture */ char host[PCAP_BUF_SIZE], ctrlport[PCAP_BUF_SIZE], iface[PCAP_BUF_SIZE]; SOCKET sockctrl; + SSL *ssl = NULL; uint8 protocol_version; /* negotiated protocol version */ + int byte_swapped; /* server is known to be byte-swapped */ int active; uint32 plen; char sendbuf[RPCAP_NETBUF_SIZE]; /* temporary buffer in which data to be sent is buffered */ @@ -2200,7 +2454,7 @@ pcap_t *pcap_open_rpcap(const char *source, int snaplen, int flags, int read_tim struct rpcap_header header; /* header of the RPCAP packet */ struct rpcap_openreply openreply; /* open reply message */ - fp = pcap_create_common(errbuf, sizeof (struct pcap_rpcap)); + fp = PCAP_CREATE_COMMON(errbuf, struct pcap_rpcap); if (fp == NULL) { return NULL; @@ -2236,13 +2490,17 @@ pcap_t *pcap_open_rpcap(const char *source, int snaplen, int flags, int read_tim * Attempt to set up the session with the server. */ if (rpcap_setup_session(fp->opt.device, auth, &active, &sockctrl, - &protocol_version, host, ctrlport, iface, errbuf) == -1) + &pr->uses_ssl, &ssl, flags, &protocol_version, &byte_swapped, + host, ctrlport, iface, errbuf) == -1) { /* Session setup failed. */ pcap_close(fp); return NULL; } + /* All good so far, save the ssl handler */ + ssl_main = ssl; + /* * Now it's time to start playing with the RPCAP protocol * RPCAP open command: create the request message @@ -2258,30 +2516,31 @@ pcap_t *pcap_open_rpcap(const char *source, int snaplen, int flags, int read_tim RPCAP_NETBUF_SIZE, SOCKBUF_BUFFERIZE, errbuf, PCAP_ERRBUF_SIZE)) goto error_nodiscard; - if (sock_send(sockctrl, sendbuf, sendbufidx, errbuf, + if (sock_send(sockctrl, ssl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) < 0) goto error_nodiscard; /* Receive and process the reply message header. */ - if (rpcap_process_msg_header(sockctrl, protocol_version, + if (rpcap_process_msg_header(sockctrl, ssl, protocol_version, RPCAP_MSG_OPEN_REQ, &header, errbuf) == -1) goto error_nodiscard; plen = header.plen; /* Read the reply body */ - if (rpcap_recv(sockctrl, (char *)&openreply, + if (rpcap_recv(sockctrl, ssl, (char *)&openreply, sizeof(struct rpcap_openreply), &plen, errbuf) == -1) goto error; /* Discard the rest of the message, if there is any. */ - if (rpcap_discard(sockctrl, plen, errbuf) == -1) + if (rpcap_discard(sockctrl, ssl, plen, errbuf) == -1) goto error_nodiscard; /* Set proper fields into the pcap_t struct */ fp->linktype = ntohl(openreply.linktype); - fp->tzoff = ntohl(openreply.tzoff); pr->rmt_sockctrl = sockctrl; + pr->ctrl_ssl = ssl; pr->protocol_version = protocol_version; + pr->byte_swapped = byte_swapped; pr->rmt_clientside = 1; /* This code is duplicated from the end of this function */ @@ -2312,11 +2571,21 @@ error: * We already reported an error; if this gets an error, just * drive on. */ - (void)rpcap_discard(sockctrl, plen, NULL); + (void)rpcap_discard(sockctrl, pr->ctrl_ssl, plen, NULL); error_nodiscard: if (!active) + { +#ifdef HAVE_OPENSSL + if (ssl) + { + // Finish using the SSL handle for the socket. + // This must be done *before* the socket is closed. + ssl_finish(ssl); + } +#endif sock_close(sockctrl, NULL, 0); + } pcap_close(fp); return NULL; @@ -2343,12 +2612,15 @@ int pcap_findalldevs_ex_remote(const char *source, struct pcap_rmtauth *auth, pcap_if_t **alldevs, char *errbuf) { uint8 protocol_version; /* protocol version */ + int byte_swapped; /* Server byte order is swapped from ours */ SOCKET sockctrl; /* socket descriptor of the control connection */ + SSL *ssl = NULL; /* optional SSL handler for sockctrl */ uint32 plen; struct rpcap_header header; /* structure that keeps the general header of the rpcap protocol */ int i, j; /* temp variables */ int nif; /* Number of interfaces listed */ int active; /* 'true' if we the other end-party is in active mode */ + uint8 uses_ssl; char host[PCAP_BUF_SIZE], port[PCAP_BUF_SIZE]; char tmpstring[PCAP_BUF_SIZE + 1]; /* Needed to convert names and descriptions from 'old' syntax to the 'new' one */ pcap_if_t *lastdev; /* Last device in the pcap_if_t list */ @@ -2361,8 +2633,9 @@ pcap_findalldevs_ex_remote(const char *source, struct pcap_rmtauth *auth, pcap_i /* * Attempt to set up the session with the server. */ - if (rpcap_setup_session(source, auth, &active, &sockctrl, - &protocol_version, host, port, NULL, errbuf) == -1) + if (rpcap_setup_session(source, auth, &active, &sockctrl, &uses_ssl, + &ssl, 0, &protocol_version, &byte_swapped, host, port, NULL, + errbuf) == -1) { /* Session setup failed. */ return -1; @@ -2372,12 +2645,12 @@ pcap_findalldevs_ex_remote(const char *source, struct pcap_rmtauth *auth, pcap_i rpcap_createhdr(&header, protocol_version, RPCAP_MSG_FINDALLIF_REQ, 0, 0); - if (sock_send(sockctrl, (char *)&header, sizeof(struct rpcap_header), + if (sock_send(sockctrl, ssl, (char *)&header, sizeof(struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) < 0) goto error_nodiscard; /* Receive and process the reply message header. */ - if (rpcap_process_msg_header(sockctrl, protocol_version, + if (rpcap_process_msg_header(sockctrl, ssl, protocol_version, RPCAP_MSG_FINDALLIF_REQ, &header, errbuf) == -1) goto error_nodiscard; @@ -2396,7 +2669,7 @@ pcap_findalldevs_ex_remote(const char *source, struct pcap_rmtauth *auth, pcap_i tmpstring2[PCAP_BUF_SIZE] = 0; /* receive the findalldevs structure from remote host */ - if (rpcap_recv(sockctrl, (char *)&findalldevs_if, + if (rpcap_recv(sockctrl, ssl, (char *)&findalldevs_if, sizeof(struct rpcap_findalldevs_if), &plen, errbuf) == -1) goto error; @@ -2440,20 +2713,20 @@ pcap_findalldevs_ex_remote(const char *source, struct pcap_rmtauth *auth, pcap_i if (findalldevs_if.namelen >= sizeof(tmpstring)) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Interface name too long"); + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Interface name too long"); goto error; } /* Retrieve adapter name */ - if (rpcap_recv(sockctrl, tmpstring, + if (rpcap_recv(sockctrl, ssl, tmpstring, findalldevs_if.namelen, &plen, errbuf) == -1) goto error; tmpstring[findalldevs_if.namelen] = 0; /* Create the new device identifier */ - if (pcap_createsrcstr(tmpstring2, PCAP_SRC_IFREMOTE, - host, port, tmpstring, errbuf) == -1) + if (pcap_createsrcstr_ex(tmpstring2, PCAP_SRC_IFREMOTE, + host, port, tmpstring, uses_ssl, errbuf) == -1) goto error; dev->name = strdup(tmpstring2); @@ -2469,12 +2742,12 @@ pcap_findalldevs_ex_remote(const char *source, struct pcap_rmtauth *auth, pcap_i { if (findalldevs_if.desclen >= sizeof(tmpstring)) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Interface description too long"); + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Interface description too long"); goto error; } /* Retrieve adapter description */ - if (rpcap_recv(sockctrl, tmpstring, + if (rpcap_recv(sockctrl, ssl, tmpstring, findalldevs_if.desclen, &plen, errbuf) == -1) goto error; @@ -2499,7 +2772,7 @@ pcap_findalldevs_ex_remote(const char *source, struct pcap_rmtauth *auth, pcap_i struct rpcap_findalldevs_ifaddr ifaddr; /* Retrieve the interface addresses */ - if (rpcap_recv(sockctrl, (char *)&ifaddr, + if (rpcap_recv(sockctrl, ssl, (char *)&ifaddr, sizeof(struct rpcap_findalldevs_ifaddr), &plen, errbuf) == -1) goto error; @@ -2573,13 +2846,21 @@ pcap_findalldevs_ex_remote(const char *source, struct pcap_rmtauth *auth, pcap_i } /* Discard the rest of the message. */ - if (rpcap_discard(sockctrl, plen, errbuf) == 1) + if (rpcap_discard(sockctrl, ssl, plen, errbuf) == 1) goto error_nodiscard; /* Control connection has to be closed only in case the remote machine is in passive mode */ if (!active) { /* DO not send RPCAP_CLOSE, since we did not open a pcap_t; no need to free resources */ +#ifdef HAVE_OPENSSL + if (ssl) + { + // Finish using the SSL handle for the socket. + // This must be done *before* the socket is closed. + ssl_finish(ssl); + } +#endif if (sock_close(sockctrl, errbuf, PCAP_ERRBUF_SIZE)) return -1; } @@ -2603,12 +2884,22 @@ error: * * Checks if all the data has been read; if not, discard the data in excess */ - (void) rpcap_discard(sockctrl, plen, NULL); + (void) rpcap_discard(sockctrl, ssl, plen, NULL); error_nodiscard: /* Control connection has to be closed only in case the remote machine is in passive mode */ if (!active) + { +#ifdef HAVE_OPENSSL + if (ssl) + { + // Finish using the SSL handle for the socket. + // This must be done *before* the socket is closed. + ssl_finish(ssl); + } +#endif sock_close(sockctrl, NULL, 0); + } /* To avoid inconsistencies in the number of sock_init() */ sock_cleanup(); @@ -2626,7 +2917,7 @@ error_nodiscard: * to implement; we provide some APIs for it that work only with rpcap. */ -SOCKET pcap_remoteact_accept(const char *address, const char *port, const char *hostlist, char *connectinghost, struct pcap_rmtauth *auth, char *errbuf) +SOCKET pcap_remoteact_accept_ex(const char *address, const char *port, const char *hostlist, char *connectinghost, struct pcap_rmtauth *auth, int uses_ssl, char *errbuf) { /* socket-related variables */ struct addrinfo hints; /* temporary struct to keep settings needed to open the new socket */ @@ -2634,7 +2925,9 @@ SOCKET pcap_remoteact_accept(const char *address, const char *port, const char * struct sockaddr_storage from; /* generic sockaddr_storage variable */ socklen_t fromlen; /* keeps the length of the sockaddr_storage variable */ SOCKET sockctrl; /* keeps the main socket identifier */ + SSL *ssl = NULL; /* Optional SSL handler for sockctrl */ uint8 protocol_version; /* negotiated protocol version */ + int byte_swapped; /* 1 if server byte order is known to be the reverse of ours */ struct activehosts *temp, *prev; /* temp var needed to scan he host list chain */ *connectinghost = 0; /* just in case */ @@ -2647,7 +2940,7 @@ SOCKET pcap_remoteact_accept(const char *address, const char *port, const char * hints.ai_socktype = SOCK_STREAM; /* Warning: this call can be the first one called by the user. */ - /* For this reason, we have to initialize the WinSock support. */ + /* For this reason, we have to initialize the Winsock support. */ if (sock_init(errbuf, PCAP_ERRBUF_SIZE) == -1) return (SOCKET)-1; @@ -2668,7 +2961,7 @@ SOCKET pcap_remoteact_accept(const char *address, const char *port, const char * } - if ((sockmain = sock_open(addrinfo, SOCKOPEN_SERVER, 1, errbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET) + if ((sockmain = sock_open(NULL, addrinfo, SOCKOPEN_SERVER, 1, errbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET) { freeaddrinfo(addrinfo); return (SOCKET)-2; @@ -2687,15 +2980,41 @@ SOCKET pcap_remoteact_accept(const char *address, const char *port, const char * if (sockctrl == INVALID_SOCKET) { - sock_geterror("accept()", errbuf, PCAP_ERRBUF_SIZE); + sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE, "accept() failed"); return (SOCKET)-2; } + /* Promote to SSL early before any error message may be sent */ + if (uses_ssl) + { +#ifdef HAVE_OPENSSL + ssl = ssl_promotion(0, sockctrl, errbuf, PCAP_ERRBUF_SIZE); + if (! ssl) + { + sock_close(sockctrl, NULL, 0); + return (SOCKET)-1; + } +#else + snprintf(errbuf, PCAP_ERRBUF_SIZE, "No TLS support"); + sock_close(sockctrl, NULL, 0); + return (SOCKET)-1; +#endif + } + /* Get the numeric for of the name of the connecting host */ if (getnameinfo((struct sockaddr *) &from, fromlen, connectinghost, RPCAP_HOSTLIST_SIZE, NULL, 0, NI_NUMERICHOST)) { - sock_geterror("getnameinfo()", errbuf, PCAP_ERRBUF_SIZE); - rpcap_senderror(sockctrl, 0, PCAP_ERR_REMOTEACCEPT, errbuf, NULL); + sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE, + "getnameinfo() failed"); + rpcap_senderror(sockctrl, ssl, 0, PCAP_ERR_REMOTEACCEPT, errbuf, NULL); +#ifdef HAVE_OPENSSL + if (ssl) + { + // Finish using the SSL handle for the socket. + // This must be done *before* the socket is closed. + ssl_finish(ssl); + } +#endif sock_close(sockctrl, NULL, 0); return (SOCKET)-1; } @@ -2703,7 +3022,15 @@ SOCKET pcap_remoteact_accept(const char *address, const char *port, const char * /* checks if the connecting host is among the ones allowed */ if (sock_check_hostlist((char *)hostlist, RPCAP_HOSTLIST_SEP, &from, errbuf, PCAP_ERRBUF_SIZE) < 0) { - rpcap_senderror(sockctrl, 0, PCAP_ERR_REMOTEACCEPT, errbuf, NULL); + rpcap_senderror(sockctrl, ssl, 0, PCAP_ERR_REMOTEACCEPT, errbuf, NULL); +#ifdef HAVE_OPENSSL + if (ssl) + { + // Finish using the SSL handle for the socket. + // This must be done *before* the socket is closed. + ssl_finish(ssl); + } +#endif sock_close(sockctrl, NULL, 0); return (SOCKET)-1; } @@ -2711,10 +3038,19 @@ SOCKET pcap_remoteact_accept(const char *address, const char *port, const char * /* * Send authentication to the remote machine. */ - if (rpcap_doauth(sockctrl, &protocol_version, auth, errbuf) == -1) + if (rpcap_doauth(sockctrl, ssl, &protocol_version, &byte_swapped, + auth, errbuf) == -1) { /* Unrecoverable error. */ - rpcap_senderror(sockctrl, 0, PCAP_ERR_REMOTEACCEPT, errbuf, NULL); + rpcap_senderror(sockctrl, ssl, 0, PCAP_ERR_REMOTEACCEPT, errbuf, NULL); +#ifdef HAVE_OPENSSL + if (ssl) + { + // Finish using the SSL handle for the socket. + // This must be done *before* the socket is closed. + ssl_finish(ssl); + } +#endif sock_close(sockctrl, NULL, 0); return (SOCKET)-3; } @@ -2751,19 +3087,34 @@ SOCKET pcap_remoteact_accept(const char *address, const char *port, const char * { pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "malloc() failed"); - rpcap_senderror(sockctrl, protocol_version, PCAP_ERR_REMOTEACCEPT, errbuf, NULL); + rpcap_senderror(sockctrl, ssl, protocol_version, PCAP_ERR_REMOTEACCEPT, errbuf, NULL); +#ifdef HAVE_OPENSSL + if (ssl) + { + // Finish using the SSL handle for the socket. + // This must be done *before* the socket is closed. + ssl_finish(ssl); + } +#endif sock_close(sockctrl, NULL, 0); return (SOCKET)-1; } memcpy(&temp->host, &from, fromlen); temp->sockctrl = sockctrl; + temp->ssl = ssl; temp->protocol_version = protocol_version; + temp->byte_swapped = byte_swapped; temp->next = NULL; return sockctrl; } +SOCKET pcap_remoteact_accept(const char *address, const char *port, const char *hostlist, char *connectinghost, struct pcap_rmtauth *auth, char *errbuf) +{ + return pcap_remoteact_accept_ex(address, port, hostlist, connectinghost, auth, 0, errbuf); +} + int pcap_remoteact_close(const char *host, char *errbuf) { struct activehosts *temp, *prev; /* temp var needed to scan the host list chain */ @@ -2779,10 +3130,10 @@ int pcap_remoteact_close(const char *host, char *errbuf) hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; - retval = getaddrinfo(host, "0", &hints, &addrinfo); + retval = sock_initaddress(host, NULL, &hints, &addrinfo, errbuf, + PCAP_ERRBUF_SIZE); if (retval != 0) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "getaddrinfo() %s", gai_strerror(retval)); return -1; } @@ -2804,7 +3155,7 @@ int pcap_remoteact_close(const char *host, char *errbuf) * Don't check for errors, since we're * just cleaning up. */ - if (sock_send(temp->sockctrl, + if (sock_send(temp->sockctrl, temp->ssl, (char *)&header, sizeof(struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) < 0) @@ -2813,12 +3164,32 @@ int pcap_remoteact_close(const char *host, char *errbuf) * Let that error be the one we * report. */ +#ifdef HAVE_OPENSSL + if (temp->ssl) + { + // Finish using the SSL handle + // for the socket. + // This must be done *before* + // the socket is closed. + ssl_finish(temp->ssl); + } +#endif (void)sock_close(temp->sockctrl, NULL, 0); status = -1; } else { +#ifdef HAVE_OPENSSL + if (temp->ssl) + { + // Finish using the SSL handle + // for the socket. + // This must be done *before* + // the socket is closed. + ssl_finish(temp->ssl); + } +#endif if (sock_close(temp->sockctrl, errbuf, PCAP_ERRBUF_SIZE) == -1) status = -1; @@ -2855,12 +3226,22 @@ int pcap_remoteact_close(const char *host, char *errbuf) /* To avoid inconsistencies in the number of sock_init() */ sock_cleanup(); - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "The host you want to close the active connection is not known"); + snprintf(errbuf, PCAP_ERRBUF_SIZE, "The host you want to close the active connection is not known"); return -1; } void pcap_remoteact_cleanup(void) { +# ifdef HAVE_OPENSSL + if (ssl_main) + { + // Finish using the SSL handle for the main active socket. + // This must be done *before* the socket is closed. + ssl_finish(ssl_main); + ssl_main = NULL; + } +# endif + /* Very dirty, but it works */ if (sockmain) { @@ -2869,7 +3250,6 @@ void pcap_remoteact_cleanup(void) /* To avoid inconsistencies in the number of sock_init() */ sock_cleanup(); } - } int pcap_remoteact_list(char *hostlist, char sep, int size, char *errbuf) @@ -2893,7 +3273,8 @@ int pcap_remoteact_list(char *hostlist, char sep, int size, char *errbuf) /* if (getnameinfo( (struct sockaddr *) &temp->host, sizeof (struct sockaddr_storage), hoststr, */ /* RPCAP_HOSTLIST_SIZE, NULL, 0, NI_NUMERICHOST) ) */ { - /* sock_geterror("getnameinfo()", errbuf, PCAP_ERRBUF_SIZE); */ + /* sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE, */ + /* "getnameinfo() failed"); */ return -1; } @@ -2901,7 +3282,7 @@ int pcap_remoteact_list(char *hostlist, char sep, int size, char *errbuf) if ((size < 0) || (len >= (size_t)size)) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "The string you provided is not able to keep " + snprintf(errbuf, PCAP_ERRBUF_SIZE, "The string you provided is not able to keep " "the hostnames for all the active connections"); return -1; } @@ -2919,11 +3300,11 @@ int pcap_remoteact_list(char *hostlist, char sep, int size, char *errbuf) /* * Receive the header of a message. */ -static int rpcap_recv_msg_header(SOCKET sock, struct rpcap_header *header, char *errbuf) +static int rpcap_recv_msg_header(SOCKET sock, SSL *ssl, struct rpcap_header *header, char *errbuf) { int nrecv; - nrecv = sock_recv(sock, (char *) header, sizeof(struct rpcap_header), + nrecv = sock_recv(sock, ssl, (char *) header, sizeof(struct rpcap_header), SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf, PCAP_ERRBUF_SIZE); if (nrecv == -1) @@ -2939,7 +3320,7 @@ static int rpcap_recv_msg_header(SOCKET sock, struct rpcap_header *header, char * Make sure the protocol version of a received message is what we were * expecting. */ -static int rpcap_check_msg_ver(SOCKET sock, uint8 expected_ver, struct rpcap_header *header, char *errbuf) +static int rpcap_check_msg_ver(SOCKET sock, SSL *ssl, uint8 expected_ver, struct rpcap_header *header, char *errbuf) { /* * Did the server specify the version we negotiated? @@ -2949,7 +3330,7 @@ static int rpcap_check_msg_ver(SOCKET sock, uint8 expected_ver, struct rpcap_hea /* * Discard the rest of the message. */ - if (rpcap_discard(sock, header->plen, errbuf) == -1) + if (rpcap_discard(sock, ssl, header->plen, errbuf) == -1) return -1; /* @@ -2957,7 +3338,7 @@ static int rpcap_check_msg_ver(SOCKET sock, uint8 expected_ver, struct rpcap_hea */ if (errbuf != NULL) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Server sent us a message with version %u when we were expecting %u", header->ver, expected_ver); } @@ -2970,7 +3351,7 @@ static int rpcap_check_msg_ver(SOCKET sock, uint8 expected_ver, struct rpcap_hea * Check the message type of a received message, which should either be * the expected message type or RPCAP_MSG_ERROR. */ -static int rpcap_check_msg_type(SOCKET sock, uint8 request_type, struct rpcap_header *header, uint16 *errcode, char *errbuf) +static int rpcap_check_msg_type(SOCKET sock, SSL *ssl, uint8 request_type, struct rpcap_header *header, uint16 *errcode, char *errbuf) { const char *request_type_string; const char *msg_type_string; @@ -2985,7 +3366,7 @@ static int rpcap_check_msg_type(SOCKET sock, uint8 request_type, struct rpcap_he * Hand that error back to our caller. */ *errcode = ntohs(header->value); - rpcap_msg_err(sock, header->plen, errbuf); + rpcap_msg_err(sock, ssl, header->plen, errbuf); return -1; } @@ -3004,7 +3385,7 @@ static int rpcap_check_msg_type(SOCKET sock, uint8 request_type, struct rpcap_he /* * Discard the rest of the message. */ - if (rpcap_discard(sock, header->plen, errbuf) == -1) + if (rpcap_discard(sock, ssl, header->plen, errbuf) == -1) return -1; /* @@ -3017,17 +3398,17 @@ static int rpcap_check_msg_type(SOCKET sock, uint8 request_type, struct rpcap_he if (request_type_string == NULL) { /* This should not happen. */ - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "rpcap_check_msg_type called for request message with type %u", request_type); return -1; } if (msg_type_string != NULL) - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s message received in response to a %s message", msg_type_string, request_type_string); else - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Message of unknown type %u message received in response to a %s request", header->type, request_type_string); } @@ -3040,11 +3421,11 @@ static int rpcap_check_msg_type(SOCKET sock, uint8 request_type, struct rpcap_he /* * Receive and process the header of a message. */ -static int rpcap_process_msg_header(SOCKET sock, uint8 expected_ver, uint8 request_type, struct rpcap_header *header, char *errbuf) +static int rpcap_process_msg_header(SOCKET sock, SSL *ssl, uint8 expected_ver, uint8 request_type, struct rpcap_header *header, char *errbuf) { uint16 errcode; - if (rpcap_recv_msg_header(sock, header, errbuf) == -1) + if (rpcap_recv_msg_header(sock, ssl, header, errbuf) == -1) { /* Network error. */ return -1; @@ -3053,13 +3434,13 @@ static int rpcap_process_msg_header(SOCKET sock, uint8 expected_ver, uint8 reque /* * Did the server specify the version we negotiated? */ - if (rpcap_check_msg_ver(sock, expected_ver, header, errbuf) == -1) + if (rpcap_check_msg_ver(sock, ssl, expected_ver, header, errbuf) == -1) return -1; /* * Check the message type. */ - return rpcap_check_msg_type(sock, request_type, header, + return rpcap_check_msg_type(sock, ssl, request_type, header, &errcode, errbuf); } @@ -3072,17 +3453,17 @@ static int rpcap_process_msg_header(SOCKET sock, uint8 expected_ver, uint8 reque * Returns 0 on success, logs a message and returns -1 on a network * error. */ -static int rpcap_recv(SOCKET sock, void *buffer, size_t toread, uint32 *plen, char *errbuf) +static int rpcap_recv(SOCKET sock, SSL *ssl, void *buffer, size_t toread, uint32 *plen, char *errbuf) { int nread; if (toread > *plen) { /* The server sent us a bad message */ - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Message payload is too short"); + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Message payload is too short"); return -1; } - nread = sock_recv(sock, buffer, toread, + nread = sock_recv(sock, ssl, buffer, toread, SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf, PCAP_ERRBUF_SIZE); if (nread == -1) { @@ -3095,7 +3476,7 @@ static int rpcap_recv(SOCKET sock, void *buffer, size_t toread, uint32 *plen, ch /* * This handles the RPCAP_MSG_ERROR message. */ -static void rpcap_msg_err(SOCKET sockctrl, uint32 plen, char *remote_errbuf) +static void rpcap_msg_err(SOCKET sockctrl, SSL *ssl, uint32 plen, char *remote_errbuf) { char errbuf[PCAP_ERRBUF_SIZE]; @@ -3105,12 +3486,14 @@ static void rpcap_msg_err(SOCKET sockctrl, uint32 plen, char *remote_errbuf) * Message is too long; just read as much of it as we * can into the buffer provided, and discard the rest. */ - if (sock_recv(sockctrl, remote_errbuf, PCAP_ERRBUF_SIZE - 1, + if (sock_recv(sockctrl, ssl, remote_errbuf, PCAP_ERRBUF_SIZE - 1, SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf, PCAP_ERRBUF_SIZE) == -1) { // Network error. - pcap_snprintf(remote_errbuf, PCAP_ERRBUF_SIZE, "Read of error message from client failed: %s", errbuf); + DIAG_OFF_FORMAT_TRUNCATION + snprintf(remote_errbuf, PCAP_ERRBUF_SIZE, "Read of error message from client failed: %s", errbuf); + DIAG_ON_FORMAT_TRUNCATION return; } @@ -3119,10 +3502,19 @@ static void rpcap_msg_err(SOCKET sockctrl, uint32 plen, char *remote_errbuf) */ remote_errbuf[PCAP_ERRBUF_SIZE - 1] = '\0'; +#ifdef _WIN32 + /* + * If we're not in UTF-8 mode, convert it to the local + * code page. + */ + if (!pcap_utf_8_mode) + utf_8_to_acp_truncated(remote_errbuf); +#endif + /* * Throw away the rest. */ - (void)rpcap_discard(sockctrl, plen - (PCAP_ERRBUF_SIZE - 1), remote_errbuf); + (void)rpcap_discard(sockctrl, ssl, plen - (PCAP_ERRBUF_SIZE - 1), remote_errbuf); } else if (plen == 0) { @@ -3131,12 +3523,14 @@ static void rpcap_msg_err(SOCKET sockctrl, uint32 plen, char *remote_errbuf) } else { - if (sock_recv(sockctrl, remote_errbuf, plen, + if (sock_recv(sockctrl, ssl, remote_errbuf, plen, SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf, PCAP_ERRBUF_SIZE) == -1) { // Network error. - pcap_snprintf(remote_errbuf, PCAP_ERRBUF_SIZE, "Read of error message from client failed: %s", errbuf); + DIAG_OFF_FORMAT_TRUNCATION + snprintf(remote_errbuf, PCAP_ERRBUF_SIZE, "Read of error message from client failed: %s", errbuf); + DIAG_ON_FORMAT_TRUNCATION return; } @@ -3153,11 +3547,11 @@ static void rpcap_msg_err(SOCKET sockctrl, uint32 plen, char *remote_errbuf) * Returns 0 on success, logs a message and returns -1 on a network * error. */ -static int rpcap_discard(SOCKET sock, uint32 len, char *errbuf) +static int rpcap_discard(SOCKET sock, SSL *ssl, uint32 len, char *errbuf) { if (len != 0) { - if (sock_discard(sock, len, errbuf, PCAP_ERRBUF_SIZE) == -1) + if (sock_discard(sock, ssl, len, errbuf, PCAP_ERRBUF_SIZE) == -1) { // Network error. return -1; @@ -3170,7 +3564,7 @@ static int rpcap_discard(SOCKET sock, uint32 len, char *errbuf) * Read bytes into the pcap_t's buffer until we have the specified * number of bytes read or we get an error or interrupt indication. */ -static int rpcap_read_packet_msg(SOCKET sock, pcap_t *p, size_t size) +static int rpcap_read_packet_msg(struct pcap_rpcap const *rp, pcap_t *p, size_t size) { u_char *bp; int cc; @@ -3189,9 +3583,10 @@ static int rpcap_read_packet_msg(SOCKET sock, pcap_t *p, size_t size) * We haven't read all of the packet header yet. * Read what remains, which could be all of it. */ - bytes_read = sock_recv(sock, bp, size - cc, + bytes_read = sock_recv(rp->rmt_sockdata, rp->data_ssl, bp, size - cc, SOCK_RECEIVEALL_NO|SOCK_EOF_IS_ERROR, p->errbuf, PCAP_ERRBUF_SIZE); + if (bytes_read == -1) { /* @@ -3220,7 +3615,7 @@ static int rpcap_read_packet_msg(SOCKET sock, pcap_t *p, size_t size) * Update the read pointer and byte count, and * return an error indication. */ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "The server terminated the connection."); return -1; } diff --git a/pcap-savefile.manfile.in b/pcap-savefile.manfile.in index 748d7afcfc55..a7ae9afbce48 100644 --- a/pcap-savefile.manfile.in +++ b/pcap-savefile.manfile.in @@ -17,7 +17,7 @@ .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" -.TH PCAP-SAVEFILE @MAN_FILE_FORMATS@ "8 March 2015" +.TH PCAP-SAVEFILE @MAN_FILE_FORMATS@ "24 April 2020" .SH NAME pcap-savefile \- libpcap savefile format .SH DESCRIPTION @@ -51,6 +51,8 @@ Link-layer header type .TE .RE .PP +The per-file header length is 24 octets. +.PP All fields in the per-file header are in the byte order of the host writing the file. Normally, the first field in the per-file header is a 4-byte magic number, with the value 0xa1b2c3d4. The magic number, when @@ -117,6 +119,8 @@ Un-truncated length of the packet data .TE .RE .PP +The per-packet header length is 16 octets. +.PP All fields in the per-packet header are in the byte order of the host writing the file. The per-packet header begins with a time stamp giving the approximate time the packet was captured; the time stamp consists of @@ -130,4 +134,4 @@ the packet not been truncated by the snapshot length. The two lengths will be equal if the number of bytes of packet data are less than or equal to the snapshot length. .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap-septel.c b/pcap-septel.c index 24cb47bb3962..6d1d904742cc 100644 --- a/pcap-septel.c +++ b/pcap-septel.c @@ -17,7 +17,6 @@ #include "pcap-int.h" -#include #include #include #include @@ -32,7 +31,6 @@ #include "pcap-septel.h" -static int septel_setfilter(pcap_t *p, struct bpf_program *fp); static int septel_stats(pcap_t *p, struct pcap_stat *ps); static int septel_getnonblock(pcap_t *p); static int septel_setnonblock(pcap_t *p, int nonblock); @@ -47,7 +45,7 @@ struct pcap_septel { /* * Read at most max_packets from the capture queue and call the callback * for each of them. Returns the number of packets handled, -1 if an - * error occured, or -2 if we were told to break out of the loop. + * error occurred, or -2 if we were told to break out of the loop. */ static int septel_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) { @@ -95,7 +93,7 @@ loop: h = GCT_grab(id); m = (MSG*)h; - /* a couter is added here to avoid an infinite loop + /* a counter is added here to avoid an infinite loop * that will cause our capture program GUI to freeze while waiting * for a packet*/ counter++ ; @@ -107,7 +105,7 @@ loop: t = h->type ; - /* catch only messages with type = 0xcf00 or 0x8f01 corrsponding to ss7 messages*/ + /* catch only messages with type = 0xcf00 or 0x8f01 corresponding to ss7 messages*/ /* XXX = why not use API_MSG_TX_REQ for 0xcf00 and API_MSG_RX_IND * for 0x8f01? */ if ((t != 0xcf00) && (t != 0x8f01)) { @@ -126,7 +124,7 @@ loop: caplen = packet_len; } /* Run the packet filter if there is one. */ - if ((p->fcode.bf_insns == NULL) || bpf_filter(p->fcode.bf_insns, dp, packet_len, caplen)) { + if ((p->fcode.bf_insns == NULL) || pcap_filter(p->fcode.bf_insns, dp, packet_len, caplen)) { /* get a time stamp , consisting of : @@ -171,7 +169,7 @@ loop: static int -septel_inject(pcap_t *handle, const void *buf _U_, size_t size _U_) +septel_inject(pcap_t *handle, const void *buf _U_, int size _U_) { pcap_strlcpy(handle->errbuf, "Sending packets isn't supported on Septel cards", PCAP_ERRBUF_SIZE); @@ -209,7 +207,7 @@ static pcap_t *septel_activate(pcap_t* handle) { handle->read_op = septel_read; handle->inject_op = septel_inject; - handle->setfilter_op = septel_setfilter; + handle->setfilter_op = install_bpf_program; handle->set_datalink_op = NULL; /* can't change data link type */ handle->getnonblock_op = septel_getnonblock; handle->setnonblock_op = septel_setnonblock; @@ -235,7 +233,7 @@ pcap_t *septel_create(const char *device, char *ebuf, int *is_ours) { /* OK, it's probably ours. */ *is_ours = 1; - p = pcap_create_common(ebuf, sizeof (struct pcap_septel)); + p = PCAP_CREATE_COMMON(ebuf, struct pcap_septel); if (p == NULL) return NULL; @@ -275,28 +273,6 @@ septel_findalldevs(pcap_if_list_t *devlistp, char *errbuf) } -/* - * Installs the given bpf filter program in the given pcap structure. There is - * no attempt to store the filter in kernel memory as that is not supported - * with Septel cards. - */ -static int septel_setfilter(pcap_t *p, struct bpf_program *fp) { - if (!p) - return -1; - if (!fp) { - strncpy(p->errbuf, "setfilter: No filter specified", - sizeof(p->errbuf)); - return -1; - } - - /* Make our private copy of the filter */ - - if (install_bpf_program(p, fp) < 0) - return -1; - - return (0); -} - /* * We don't support non-blocking mode. I'm not sure what we'd * do to support it and, given that we don't support select()/ @@ -338,7 +314,7 @@ pcap_platform_finddevs(pcap_if_list_t *devlistp, char *errbuf) pcap_t * pcap_create_interface(const char *device, char *errbuf) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "This version of libpcap only supports Septel cards"); return (NULL); } diff --git a/pcap-sita.c b/pcap-sita.c index b9dda9c55b48..2e5d4426adb7 100644 --- a/pcap-sita.c +++ b/pcap-sita.c @@ -72,6 +72,14 @@ typedef struct unit { int len; /* the current size of the inbound message */ } unit_t; +/* + * Private data. + * Currently contains nothing. + */ +struct pcap_sita { + int dummy; +}; + static unit_t units[MAX_CHASSIS+1][MAX_GEOSLOT+1]; /* we use indexes of 1 through 8, but we reserve/waste index 0 */ static fd_set readfds; /* a place to store the file descriptors for the connections to the IOPs */ static int max_fs; @@ -266,7 +274,7 @@ int acn_parse_hosts_file(char *errbuf) { /* returns: -1 = error, 0 = OK */ empty_unit_table(); if ((fp = fopen("/etc/hosts", "r")) == NULL) { /* try to open the hosts file and if it fails */ - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Cannot open '/etc/hosts' for reading."); /* return the nohostsfile error response */ + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Cannot open '/etc/hosts' for reading."); /* return the nohostsfile error response */ return -1; } while (fgets(buf, MAX_LINE_SIZE-1, fp)) { /* while looping over the file */ @@ -289,15 +297,15 @@ int acn_parse_hosts_file(char *errbuf) { /* returns: -1 = error, 0 = OK */ geoslot = *(ptr2 + 5) - '0'; /* and geo-slot number */ if (chassis < 1 || chassis > MAX_CHASSIS || geoslot < 1 || geoslot > MAX_GEOSLOT) { /* if the chassis and/or slot numbers appear to be bad... */ - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Invalid ACN name in '/etc/hosts'."); /* warn the user */ + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Invalid ACN name in '/etc/hosts'."); /* warn the user */ continue; /* and ignore the entry */ } - if ((ptr2 = (char *)malloc(strlen(ptr) + 1)) == NULL) { + ptr2 = strdup(ptr); /* copy the IP address into our malloc'ed memory */ + if (ptr2 == NULL) { pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "malloc"); continue; } - strcpy(ptr2, ptr); /* copy the IP address into our malloc'ed memory */ u = &units[chassis][geoslot]; u->ip = ptr2; /* and remember the whole shebang */ u->chassis = chassis; @@ -407,19 +415,18 @@ static void acn_freealldevs(void) { static void nonUnified_IOP_port_name(char *buf, size_t bufsize, const char *proto, unit_t *u) { - pcap_snprintf(buf, bufsize, "%s_%d_%d", proto, u->chassis, u->geoslot); + snprintf(buf, bufsize, "%s_%d_%d", proto, u->chassis, u->geoslot); } static void unified_IOP_port_name(char *buf, size_t bufsize, const char *proto, unit_t *u, int IOPportnum) { int portnum; portnum = ((u->chassis - 1) * 64) + ((u->geoslot - 1) * 8) + IOPportnum + 1; - pcap_snprintf(buf, bufsize, "%s_%d", proto, portnum); + snprintf(buf, bufsize, "%s_%d", proto, portnum); } static char *translate_IOP_to_pcap_name(unit_t *u, char *IOPname, bpf_u_int32 iftype) { iface_t *iface_ptr, *iface; - char *name; char buf[32]; char *proto; char *port; @@ -434,15 +441,12 @@ static char *translate_IOP_to_pcap_name(unit_t *u, char *IOPname, bpf_u_int32 if iface->iftype = iftype; /* remember the interface type of this interface */ - name = malloc(strlen(IOPname) + 1); /* get memory for the IOP's name */ - if (name == NULL) { /* oops, we didn't get the memory requested */ + iface->IOPname = strdup(IOPname); /* copy it and stick it into the structure */ + if (iface->IOPname == NULL) { /* oops, we didn't get the memory requested */ fprintf(stderr, "Error...couldn't allocate memory for IOPname...value of errno is: %d\n", errno); return NULL; } - strcpy(name, IOPname); /* and copy it in */ - iface->IOPname = name; /* and stick it into the structure */ - if (strncmp(IOPname, "lo", 2) == 0) { IOPportnum = atoi(&IOPname[2]); switch (iftype) { @@ -478,20 +482,17 @@ static char *translate_IOP_to_pcap_name(unit_t *u, char *IOPname, bpf_u_int32 if return NULL; } - name = malloc(strlen(buf) + 1); /* get memory for that name */ - if (name == NULL) { /* oops, we didn't get the memory requested */ + iface->name = strdup(buf); /* make a copy and stick it into the structure */ + if (iface->name == NULL) { /* oops, we didn't get the memory requested */ fprintf(stderr, "Error...couldn't allocate memory for IOP port name...value of errno is: %d\n", errno); return NULL; } - strcpy(name, buf); /* and copy it in */ - iface->name = name; /* and stick it into the structure */ - if (u->iface == 0) { /* if this is the first name */ u->iface = iface; /* stick this entry at the head of the list */ } else { iface_ptr = u->iface; - while (iface_ptr->next) { /* othewise scan the list */ + while (iface_ptr->next) { /* otherwise scan the list */ iface_ptr = iface_ptr->next; /* till we're at the last entry */ } iface_ptr->next = iface; /* then tack this entry on the end of the list */ @@ -703,7 +704,7 @@ static int process_client_data (char *errbuf) { /* returns: -1 = error, 0 prev_iff = iff; newname = translate_IOP_to_pcap_name(u, iff->name, interfaceType); /* add a translation entry and get a point to the mangled name */ - bigger_buffer = realloc(iff->name, strlen(newname) + 1)); + bigger_buffer = realloc(iff->name, strlen(newname) + 1); if (bigger_buffer == NULL) { /* we now re-write the name stored in the interface list */ pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "realloc"); @@ -754,9 +755,9 @@ static void wait_for_all_answers(void) { memcpy(&working_set, &readfds, sizeof(readfds)); /* otherwise, we still have to listen for more stuff, till we timeout */ retval = select(max_fs + 1, &working_set, NULL, NULL, &tv); - if (retval == -1) { /* an error occured !!!!! */ + if (retval == -1) { /* an error occurred !!!!! */ return; - } else if (retval == 0) { /* timeout occured, so process what we've got sofar and return */ + } else if (retval == 0) { /* timeout occurred, so process what we've got sofar and return */ printf("timeout\n"); return; } else { @@ -883,7 +884,7 @@ static void acn_start_monitor(int fd, int snaplen, int timeout, int promiscuous, //printf("acn_start_monitor() complete\n"); // fulko } -static int pcap_inject_acn(pcap_t *p, const void *buf _U_, size_t size _U_) { +static int pcap_inject_acn(pcap_t *p, const void *buf _U_, int size _U_) { pcap_strlcpy(p->errbuf, "Sending packets isn't supported on ACN adapters", PCAP_ERRBUF_SIZE); return (-1); @@ -915,12 +916,6 @@ static int pcap_setfilter_acn(pcap_t *handle, struct bpf_program *bpf) { return 0; } -static int pcap_setdirection_acn(pcap_t *handle, pcap_direction_t d) { - pcap_snprintf(handle->errbuf, sizeof(handle->errbuf), - "Setting direction is not supported on ACN adapters"); - return -1; -} - static int acn_read_n_bytes_with_timeout(pcap_t *handle, int count) { struct timeval tv; int retval, fd; @@ -940,10 +935,10 @@ static int acn_read_n_bytes_with_timeout(pcap_t *handle, int count) { bp = handle->bp; while (count) { retval = select(fd + 1, &w_fds, NULL, NULL, &tv); - if (retval == -1) { /* an error occured !!!!! */ + if (retval == -1) { /* an error occurred !!!!! */ // fprintf(stderr, "error during packet data read\n"); - return -1; /* but we need to return a good indication to prevent unneccessary popups */ - } else if (retval == 0) { /* timeout occured, so process what we've got sofar and return */ + return -1; /* but we need to return a good indication to prevent unnecessary popups */ + } else if (retval == 0) { /* timeout occurred, so process what we've got sofar and return */ // fprintf(stderr, "timeout during packet data read\n"); return -1; } else { @@ -997,7 +992,7 @@ static int pcap_activate_sita(pcap_t *handle) { handle->inject_op = pcap_inject_acn; handle->setfilter_op = pcap_setfilter_acn; - handle->setdirection_op = pcap_setdirection_acn; + handle->setdirection_op = NULL; /* Not implemented */ handle->set_datalink_op = NULL; /* can't change data link type */ handle->getnonblock_op = pcap_getnonblock_fd; handle->setnonblock_op = pcap_setnonblock_fd; @@ -1046,7 +1041,7 @@ static int pcap_activate_sita(pcap_t *handle) { pcap_t *pcap_create_interface(const char *device _U_, char *ebuf) { pcap_t *p; - p = pcap_create_common(ebuf, 0); + p = PCAP_CREATE_COMMON(ebuf, struct pcap_sita); if (p == NULL) return (NULL); diff --git a/pcap-sita.html b/pcap-sita.html index 4a8fe1a27e8a..33f1e10fa52c 100644 --- a/pcap-sita.html +++ b/pcap-sita.html @@ -22,7 +22,7 @@ A { text-decoration:none }
    Note: This document is part of the libpcap Git and was derived from 'pcap.3' (circa Aug/07).

    - The ACN provides a customized/distributed version of this library that alows SMPs to + The ACN provides a customized/distributed version of this library that allows SMPs to interact with the various IOPs within the site providing a standard mechanism to capture LAN and WAN message traffic.

    @@ -31,7 +31,7 @@ A { text-decoration:none } SMP The Supervisory Management Processor where Wireshark (or equivalent) - runs in conjuction with a libpcap front-end. + runs in conjunction with a libpcap front-end. IOP @@ -43,7 +43,7 @@ A { text-decoration:none }

    Each IOP will be capable of supporting multiple connections from an SMP enabling monitoring of more than one interface at a time, each through - its own seperate connection. The IOP is responsible to ensure and report + its own separate connection. The IOP is responsible to ensure and report an error if any attempt is made to monitor the same interface more than once.

    There are three applications that will be supported by the ACN version of libpcap. @@ -121,8 +121,8 @@ A { text-decoration:none } IOP -> SMP After any required processing is complete, the IOP will return a - null terminated string containing an error message if one occured. - If no error occured, a empty string is still returned. + null terminated string containing an error message if one occurred. + If no error occurred, a empty string is still returned. Errors are:

    • "Interface (xxx) does not exist." @@ -298,7 +298,7 @@ A { text-decoration:none } The SMP reads only the next packet from the reverse channel of the connection between the SMP and the IOP that provides the captured data (via calling pcap_dispatch() - with a count of 1) and returns seperate pointers to both the + with a count of 1) and returns separate pointers to both the packet header and packet data by invoking an internal callback. @@ -322,7 +322,7 @@ A { text-decoration:none } IOP -> SMP The IOP returns a null terminated error string if it failed to accept the filter. - If no error occured, then a NULL terminated empty string is returned instead. + If no error occurred, then a NULL terminated empty string is returned instead. Errors are:
      • "Invalid BPF." @@ -362,7 +362,7 @@ A { text-decoration:none } SMP -> IOP The SMP closes the file descriptor, and if the descriptor is that of - the comminucation session with an IOP, it too is terminated. + the communication session with an IOP, it too is terminated. IOP @@ -370,7 +370,7 @@ A { text-decoration:none } If the IOP detects that its communication session with an SMP has closed, it will terminate any monitoring in progress, release any resources and close its end of the session. - It will not maintain persistance of any information or prior mode of operation. + It will not maintain persistence of any information or prior mode of operation. @@ -442,7 +442,7 @@ A { text-decoration:none } IOP -> SMP The IOP returns a list of sequences of information as defined by the return parameter of this function call (as shown in the following table). - Elements are specified by providing an unsigned byte preceeding the actual data that contains length information. + Elements are specified by providing an unsigned byte preceding the actual data that contains length information.

        @@ -635,7 +635,7 @@ A { text-decoration:none }
        BPF program 'n' 8 bytes of each command (repeated 'n' times).
        - Each command consists of that C-style structure which contains: + Each command consists of that C-style structure which contains:

        @@ -719,7 +719,7 @@ A { text-decoration:none } -
        ps_ifdrop 4The number of packets dropped by the network inteface + The number of packets dropped by the network interface (regardless of whether they would have passed the input filter).
        @@ -839,7 +839,7 @@ A { text-decoration:none }

          PCAP, Wireshark and Tcpdump enhancements have been added to the ACN to support monitoring of its ports, however each of these facilities were focused on capturing - and displaying traffic from LAN interfaces. The SITA extentions to these facilities + and displaying traffic from LAN interfaces. The SITA extensions to these facilities are used to also provide the ability to capture, filter, and display information from an ACN's WAN ports.

          @@ -849,7 +849,7 @@ A { text-decoration:none }

          • For Ethernet (like) devices, the packet format is unchanged from the standard Pcap format. -
          • For WAN devices, the packet contains a 5 byte header that preceeds the actual captured data +
          • For WAN devices, the packet contains a 5 byte header that precedes the actual captured data described by the following table:

          diff --git a/pcap-snf.c b/pcap-snf.c index 50d92cd9e217..b885e02657c1 100644 --- a/pcap-snf.c +++ b/pcap-snf.c @@ -9,8 +9,8 @@ #include #include #include +#include /* for INT_MAX */ -#include #ifndef _WIN32 #include #include @@ -140,9 +140,24 @@ snf_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) if (!p) return -1; + /* + * 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(cnt)) + cnt = INT_MAX; + n = 0; timeout = ps->snf_timeout; - while (n < cnt || PACKET_COUNT_IS_UNLIMITED(cnt)) { + while (n < cnt) { /* * Has "pcap_breakloop()" been called? */ @@ -177,7 +192,7 @@ snf_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) caplen = p->snapshot; if ((p->fcode.bf_insns == NULL) || - bpf_filter(p->fcode.bf_insns, req.pkt_addr, req.length, caplen)) { + pcap_filter(p->fcode.bf_insns, req.pkt_addr, req.length, caplen)) { hdr.ts = snf_timestamp_to_timeval(req.timestamp, p->opt.tstamp_precision); hdr.caplen = caplen; hdr.len = req.length; @@ -194,26 +209,7 @@ snf_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) } static int -snf_setfilter(pcap_t *p, struct bpf_program *fp) -{ - if (!p) - return -1; - if (!fp) { - strncpy(p->errbuf, "setfilter: No filter specified", - sizeof(p->errbuf)); - return -1; - } - - /* Make our private copy of the filter */ - - if (install_bpf_program(p, fp) < 0) - return -1; - - return (0); -} - -static int -snf_inject(pcap_t *p, const void *buf _U_, size_t size _U_) +snf_inject(pcap_t *p, const void *buf _U_, int size _U_) { #ifdef SNF_HAVE_INJECT_API struct pcap_snf *ps = p->priv; @@ -253,7 +249,7 @@ snf_activate(pcap_t* p) int flags = -1, ring_id = -1; if (device == NULL) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "device is NULL"); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "device is NULL"); return -1; } @@ -327,7 +323,7 @@ snf_activate(pcap_t* p) p->linktype = DLT_EN10MB; p->read_op = snf_read; p->inject_op = snf_inject; - p->setfilter_op = snf_setfilter; + p->setfilter_op = install_bpf_program; p->setdirection_op = NULL; /* Not implemented.*/ p->set_datalink_op = snf_set_datalink; p->getnonblock_op = snf_getnonblock; @@ -355,7 +351,7 @@ snf_findalldevs(pcap_if_list_t *devlistp, char *errbuf) const char *nr = NULL; if (snf_init(SNF_VERSION_API)) { - (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, "snf_getifaddrs: snf_init failed"); return (-1); } @@ -370,7 +366,7 @@ snf_findalldevs(pcap_if_list_t *devlistp, char *errbuf) errno = 0; merge = strtol(nr, NULL, 0); if (errno) { - (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, "snf_getifaddrs: SNF_FLAGS is not a valid number"); return (-1); } @@ -408,7 +404,7 @@ snf_findalldevs(pcap_if_list_t *devlistp, char *errbuf) * entry for the device, if they're not already in the * list of IP addresses for the device? */ - (void)pcap_snprintf(desc,MAX_DESC_LENGTH,"Myricom %ssnf%d", + (void)snprintf(desc,MAX_DESC_LENGTH,"Myricom %ssnf%d", merge ? "Merge Bitmask Port " : "", merge ? 1 << ifa->snf_ifa_portnum : ifa->snf_ifa_portnum); /* @@ -484,8 +480,8 @@ snf_findalldevs(pcap_if_list_t *devlistp, char *errbuf) /* * Add a new entry with all ports bitmask */ - (void)pcap_snprintf(name,MAX_DESC_LENGTH,"snf%d",allports); - (void)pcap_snprintf(desc,MAX_DESC_LENGTH,"Myricom Merge Bitmask All Ports snf%d", + (void)snprintf(name,MAX_DESC_LENGTH,"snf%d",allports); + (void)snprintf(desc,MAX_DESC_LENGTH,"Myricom Merge Bitmask All Ports snf%d", allports); /* * XXX - is there any notion of "up" and "running" that @@ -560,7 +556,7 @@ snf_create(const char *device, char *ebuf, int *is_ours) /* OK, it's probably ours. */ *is_ours = 1; - p = pcap_create_common(ebuf, sizeof (struct pcap_snf)); + p = PCAP_CREATE_COMMON(ebuf, struct pcap_snf); if (p == NULL) return NULL; ps = p->priv; @@ -568,7 +564,6 @@ snf_create(const char *device, char *ebuf, int *is_ours) /* * We support microsecond and nanosecond time stamps. */ - p->tstamp_precision_count = 2; p->tstamp_precision_list = malloc(2 * sizeof(u_int)); if (p->tstamp_precision_list == NULL) { pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, errno, @@ -578,6 +573,7 @@ snf_create(const char *device, char *ebuf, int *is_ours) } p->tstamp_precision_list[0] = PCAP_TSTAMP_PRECISION_MICRO; p->tstamp_precision_list[1] = PCAP_TSTAMP_PRECISION_NANO; + p->tstamp_precision_count = 2; p->activate_op = snf_activate; ps->snf_boardnum = boardnum; @@ -605,7 +601,7 @@ pcap_platform_finddevs(pcap_if_list_t *devlistp, char *errbuf) pcap_t * pcap_create_interface(const char *device, char *errbuf) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "This version of libpcap only supports SNF cards"); return NULL; } diff --git a/pcap-snit.c b/pcap-snit.c index 9c6fbd48e7b8..3f4e69d71863 100644 --- a/pcap-snit.c +++ b/pcap-snit.c @@ -53,7 +53,6 @@ #include #include -#include #include #include #include @@ -140,6 +139,9 @@ pcap_read_snit(pcap_t *p, int cnt, pcap_handler callback, u_char *user) /* * loop through each snapshot in the chunk + * + * This assumes that a single buffer of packets will have + * <= INT_MAX packets, so the packet count doesn't overflow. */ n = 0; ep = bp + cc; @@ -190,7 +192,7 @@ pcap_read_snit(pcap_t *p, int cnt, pcap_handler callback, u_char *user) if (caplen > p->snapshot) caplen = p->snapshot; - if (bpf_filter(p->fcode.bf_insns, cp, nlp->nh_pktlen, caplen)) { + if (pcap_filter(p->fcode.bf_insns, cp, nlp->nh_pktlen, caplen)) { struct pcap_pkthdr h; h.ts = ntp->nh_timestamp; h.len = nlp->nh_pktlen; @@ -208,7 +210,7 @@ pcap_read_snit(pcap_t *p, int cnt, pcap_handler callback, u_char *user) } static int -pcap_inject_snit(pcap_t *p, const void *buf, size_t size) +pcap_inject_snit(pcap_t *p, const void *buf, int size) { struct strbuf ctl, data; @@ -332,12 +334,16 @@ pcap_activate_snit(pcap_t *p) if (fd < 0 && errno == EACCES) p->fd = fd = open(dev, O_RDONLY); if (fd < 0) { - if (errno == EACCES) + if (errno == EACCES) { err = PCAP_ERROR_PERM_DENIED; - else + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Attempt to open %s failed with EACCES - root privileges may be required", + dev); + } else { err = PCAP_ERROR; - pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, - errno, "%s", dev); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "%s", dev); + } goto bad; } @@ -460,7 +466,7 @@ pcap_create_interface(const char *device _U_, char *ebuf) { pcap_t *p; - p = pcap_create_common(ebuf, sizeof (struct pcap_snit)); + p = PCAP_CREATE_COMMON(ebuf, struct pcap_snit); if (p == NULL) return (NULL); diff --git a/pcap-snoop.c b/pcap-snoop.c index a598bae5fe1f..2f44b1dd91bb 100644 --- a/pcap-snoop.c +++ b/pcap-snoop.c @@ -126,7 +126,7 @@ again: } if (p->fcode.bf_insns == NULL || - bpf_filter(p->fcode.bf_insns, cp, datalen, caplen)) { + pcap_filter(p->fcode.bf_insns, cp, datalen, caplen)) { struct pcap_pkthdr h; ++psn->stat.ps_recv; h.ts.tv_sec = sh->snoop_timestamp.tv_sec; @@ -140,7 +140,7 @@ again: } static int -pcap_inject_snoop(pcap_t *p, const void *buf, size_t size) +pcap_inject_snoop(pcap_t *p, const void *buf, int size) { int ret; @@ -310,7 +310,7 @@ pcap_activate_snoop(pcap_t *p) p->linktype = DLT_NULL; ll_hdrlen = 4; } else { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "snoop: unknown physical layer type"); goto bad; } @@ -421,7 +421,7 @@ pcap_create_interface(const char *device _U_, char *ebuf) { pcap_t *p; - p = pcap_create_common(ebuf, sizeof (struct pcap_snoop)); + p = PCAP_CREATE_COMMON(ebuf, struct pcap_snoop); if (p == NULL) return (NULL); diff --git a/pcap-tc.c b/pcap-tc.c index 65fb0e2b4aa7..1d753b5477ad 100644 --- a/pcap-tc.c +++ b/pcap-tc.c @@ -126,7 +126,6 @@ static void TcCleanup(pcap_t *p); static int TcInject(pcap_t *p, const void *buf, int size); static int TcRead(pcap_t *p, int cnt, pcap_handler callback, u_char *user); static int TcStats(pcap_t *p, struct pcap_stat *ps); -static int TcSetFilter(pcap_t *p, struct bpf_program *fp); #ifdef _WIN32 static struct pcap_stat *TcStatsEx(pcap_t *p, int *pcap_stat_size); static int TcSetBuff(pcap_t *p, int dim); @@ -253,58 +252,6 @@ typedef struct _PPI_HEADER #pragma pack(pop) #ifdef _WIN32 -// -// This wrapper around loadlibrary appends the system folder (usually c:\windows\system32) -// to the relative path of the DLL, so that the DLL is always loaded from an absolute path -// (It's no longer possible to load airpcap.dll from the application folder). -// This solves the DLL Hijacking issue discovered in August 2010 -// http://blog.metasploit.com/2010/08/exploiting-dll-hijacking-flaws.html -// -HMODULE LoadLibrarySafe(LPCTSTR lpFileName) -{ - TCHAR path[MAX_PATH]; - TCHAR fullFileName[MAX_PATH]; - UINT res; - HMODULE hModule = NULL; - do - { - res = GetSystemDirectory(path, MAX_PATH); - - if (res == 0) - { - // - // some bad failure occurred; - // - break; - } - - if (res > MAX_PATH) - { - // - // the buffer was not big enough - // - SetLastError(ERROR_INSUFFICIENT_BUFFER); - break; - } - - if (res + 1 + _tcslen(lpFileName) + 1 < MAX_PATH) - { - memcpy(fullFileName, path, res * sizeof(TCHAR)); - fullFileName[res] = _T('\\'); - memcpy(&fullFileName[res + 1], lpFileName, (_tcslen(lpFileName) + 1) * sizeof(TCHAR)); - - hModule = LoadLibrary(fullFileName); - } - else - { - SetLastError(ERROR_INSUFFICIENT_BUFFER); - } - - }while(FALSE); - - return hModule; -} - /* * NOTE: this function should be called by the pcap functions that can theoretically * deal with the Tc library for the first time, namely listing the adapters and @@ -341,34 +288,34 @@ TC_API_LOAD_STATUS LoadTcFunctions(void) currentStatus = TC_API_CANNOT_LOAD; - g_TcFunctions.hTcApiDllHandle = LoadLibrarySafe("TcApi.dll"); + g_TcFunctions.hTcApiDllHandle = pcap_load_code("TcApi.dll"); if (g_TcFunctions.hTcApiDllHandle == NULL) break; - g_TcFunctions.QueryPortList = (TcFcnQueryPortList) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcQueryPortList"); - g_TcFunctions.FreePortList = (TcFcnFreePortList) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcFreePortList"); + g_TcFunctions.QueryPortList = (TcFcnQueryPortList) pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcQueryPortList"); + g_TcFunctions.FreePortList = (TcFcnFreePortList) pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcFreePortList"); - g_TcFunctions.StatusGetString = (TcFcnStatusGetString) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcStatusGetString"); + g_TcFunctions.StatusGetString = (TcFcnStatusGetString) pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcStatusGetString"); - g_TcFunctions.PortGetName = (TcFcnPortGetName) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcPortGetName"); - g_TcFunctions.PortGetDescription = (TcFcnPortGetDescription) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcPortGetDescription"); + g_TcFunctions.PortGetName = (TcFcnPortGetName) pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcPortGetName"); + g_TcFunctions.PortGetDescription = (TcFcnPortGetDescription) pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcPortGetDescription"); - g_TcFunctions.InstanceOpenByName = (TcFcnInstanceOpenByName) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceOpenByName"); - g_TcFunctions.InstanceClose = (TcFcnInstanceClose) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceClose"); - g_TcFunctions.InstanceSetFeature = (TcFcnInstanceSetFeature) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceSetFeature"); - g_TcFunctions.InstanceQueryFeature = (TcFcnInstanceQueryFeature) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceQueryFeature"); - g_TcFunctions.InstanceReceivePackets = (TcFcnInstanceReceivePackets) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceReceivePackets"); - g_TcFunctions.InstanceGetReceiveWaitHandle = (TcFcnInstanceGetReceiveWaitHandle)GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceGetReceiveWaitHandle"); - g_TcFunctions.InstanceTransmitPackets = (TcFcnInstanceTransmitPackets)GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceTransmitPackets"); - g_TcFunctions.InstanceQueryStatistics = (TcFcnInstanceQueryStatistics)GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcInstanceQueryStatistics"); + g_TcFunctions.InstanceOpenByName = (TcFcnInstanceOpenByName) pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcInstanceOpenByName"); + g_TcFunctions.InstanceClose = (TcFcnInstanceClose) pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcInstanceClose"); + g_TcFunctions.InstanceSetFeature = (TcFcnInstanceSetFeature) pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcInstanceSetFeature"); + g_TcFunctions.InstanceQueryFeature = (TcFcnInstanceQueryFeature) pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcInstanceQueryFeature"); + g_TcFunctions.InstanceReceivePackets = (TcFcnInstanceReceivePackets) pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcInstanceReceivePackets"); + g_TcFunctions.InstanceGetReceiveWaitHandle = (TcFcnInstanceGetReceiveWaitHandle)pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcInstanceGetReceiveWaitHandle"); + g_TcFunctions.InstanceTransmitPackets = (TcFcnInstanceTransmitPackets)pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcInstanceTransmitPackets"); + g_TcFunctions.InstanceQueryStatistics = (TcFcnInstanceQueryStatistics)pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcInstanceQueryStatistics"); - g_TcFunctions.PacketsBufferCreate = (TcFcnPacketsBufferCreate) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcPacketsBufferCreate"); - g_TcFunctions.PacketsBufferDestroy = (TcFcnPacketsBufferDestroy) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcPacketsBufferDestroy"); - g_TcFunctions.PacketsBufferQueryNextPacket = (TcFcnPacketsBufferQueryNextPacket)GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcPacketsBufferQueryNextPacket"); - g_TcFunctions.PacketsBufferCommitNextPacket = (TcFcnPacketsBufferCommitNextPacket)GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcPacketsBufferCommitNextPacket"); + g_TcFunctions.PacketsBufferCreate = (TcFcnPacketsBufferCreate) pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcPacketsBufferCreate"); + g_TcFunctions.PacketsBufferDestroy = (TcFcnPacketsBufferDestroy) pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcPacketsBufferDestroy"); + g_TcFunctions.PacketsBufferQueryNextPacket = (TcFcnPacketsBufferQueryNextPacket)pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcPacketsBufferQueryNextPacket"); + g_TcFunctions.PacketsBufferCommitNextPacket = (TcFcnPacketsBufferCommitNextPacket)pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcPacketsBufferCommitNextPacket"); - g_TcFunctions.StatisticsDestroy = (TcFcnStatisticsDestroy) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcStatisticsDestroy"); - g_TcFunctions.StatisticsUpdate = (TcFcnStatisticsUpdate) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcStatisticsUpdate"); - g_TcFunctions.StatisticsQueryValue = (TcFcnStatisticsQueryValue) GetProcAddress(g_TcFunctions.hTcApiDllHandle, "TcStatisticsQueryValue"); + g_TcFunctions.StatisticsDestroy = (TcFcnStatisticsDestroy) pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcStatisticsDestroy"); + g_TcFunctions.StatisticsUpdate = (TcFcnStatisticsUpdate) pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcStatisticsUpdate"); + g_TcFunctions.StatisticsQueryValue = (TcFcnStatisticsQueryValue) pcap_find_function(g_TcFunctions.hTcApiDllHandle, "TcStatisticsQueryValue"); if ( g_TcFunctions.QueryPortList == NULL || g_TcFunctions.FreePortList == NULL @@ -552,7 +499,7 @@ TcActivate(pcap_t *p) if (pt->PpiPacket == NULL) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Error allocating memory"); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Error allocating memory"); return PCAP_ERROR; } @@ -587,7 +534,7 @@ TcActivate(pcap_t *p) if (status != TC_SUCCESS) { /* Adapter detected but we are not able to open it. Return failure. */ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Error opening TurboCap adapter: %s", g_TcFunctions.StatusGetString(status)); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Error opening TurboCap adapter: %s", g_TcFunctions.StatusGetString(status)); return PCAP_ERROR; } @@ -619,7 +566,7 @@ TcActivate(pcap_t *p) if (status != TC_SUCCESS) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,"Error enabling reception on a TurboCap instance: %s", g_TcFunctions.StatusGetString(status)); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE,"Error enabling reception on a TurboCap instance: %s", g_TcFunctions.StatusGetString(status)); goto bad; } @@ -658,12 +605,12 @@ TcActivate(pcap_t *p) if (status != TC_SUCCESS) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,"Error setting the read timeout a TurboCap instance: %s", g_TcFunctions.StatusGetString(status)); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE,"Error setting the read timeout a TurboCap instance: %s", g_TcFunctions.StatusGetString(status)); goto bad; } p->read_op = TcRead; - p->setfilter_op = TcSetFilter; + p->setfilter_op = install_bpf_program; p->setdirection_op = NULL; /* Not implemented. */ p->set_datalink_op = TcSetDatalink; p->getnonblock_op = TcGetNonBlock; @@ -756,7 +703,7 @@ TcCreate(const char *device, char *ebuf, int *is_ours) /* OK, it's probably ours. */ *is_ours = 1; - p = pcap_create_common(ebuf, sizeof (struct pcap_tc)); + p = PCAP_CREATE_COMMON(ebuf, struct pcap_tc); if (p == NULL) return NULL; @@ -791,14 +738,14 @@ static int TcSetDatalink(pcap_t *p, int dlt) static int TcGetNonBlock(pcap_t *p) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Non-blocking mode isn't supported for TurboCap ports"); return -1; } static int TcSetNonBlock(pcap_t *p, int nonblock) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Non-blocking mode isn't supported for TurboCap ports"); return -1; } @@ -840,7 +787,7 @@ static int TcInject(pcap_t *p, const void *buf, int size) if (size >= 0xFFFF) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send error: the TurboCap API does not support packets larger than 64k"); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send error: the TurboCap API does not support packets larger than 64k"); return -1; } @@ -848,7 +795,7 @@ static int TcInject(pcap_t *p, const void *buf, int size) if (status != TC_SUCCESS) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send error: TcPacketsBufferCreate failure: %s (%08x)", g_TcFunctions.StatusGetString(status), status); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send error: TcPacketsBufferCreate failure: %s (%08x)", g_TcFunctions.StatusGetString(status), status); return -1; } @@ -868,12 +815,12 @@ static int TcInject(pcap_t *p, const void *buf, int size) if (status != TC_SUCCESS) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send error: TcInstanceTransmitPackets failure: %s (%08x)", g_TcFunctions.StatusGetString(status), status); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send error: TcInstanceTransmitPackets failure: %s (%08x)", g_TcFunctions.StatusGetString(status), status); } } else { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send error: TcPacketsBufferCommitNextPacket failure: %s (%08x)", g_TcFunctions.StatusGetString(status), status); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send error: TcPacketsBufferCommitNextPacket failure: %s (%08x)", g_TcFunctions.StatusGetString(status), status); } g_TcFunctions.PacketsBufferDestroy(buffer); @@ -913,7 +860,7 @@ static int TcRead(pcap_t *p, int cnt, pcap_handler callback, u_char *user) status = g_TcFunctions.InstanceReceivePackets(pt->TcInstance, &pt->TcPacketsBuffer); if (status != TC_SUCCESS) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "read error, TcInstanceReceivePackets failure: %s (%08x)", g_TcFunctions.StatusGetString(status), status); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "read error, TcInstanceReceivePackets failure: %s (%08x)", g_TcFunctions.StatusGetString(status), status); return -1; } } @@ -963,14 +910,14 @@ static int TcRead(pcap_t *p, int cnt, pcap_handler callback, u_char *user) if (status != TC_SUCCESS) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "read error, TcPacketsBufferQueryNextPacket failure: %s (%08x)", g_TcFunctions.StatusGetString(status), status); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "read error, TcPacketsBufferQueryNextPacket failure: %s (%08x)", g_TcFunctions.StatusGetString(status), status); return -1; } - /* No underlaying filtering system. We need to filter on our own */ + /* No underlying filtering system. We need to filter on our own */ if (p->fcode.bf_insns) { - filterResult = bpf_filter(p->fcode.bf_insns, data, tcHeader.Length, tcHeader.CapturedLength); + filterResult = pcap_filter(p->fcode.bf_insns, data, tcHeader.Length, tcHeader.CapturedLength); if (filterResult == 0) { @@ -1053,7 +1000,7 @@ TcStats(pcap_t *p, struct pcap_stat *ps) if (status != TC_SUCCESS) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error in TcInstanceQueryStatistics: %s (%08x)", g_TcFunctions.StatusGetString(status), status); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error in TcInstanceQueryStatistics: %s (%08x)", g_TcFunctions.StatusGetString(status), status); return -1; } @@ -1062,7 +1009,7 @@ TcStats(pcap_t *p, struct pcap_stat *ps) status = g_TcFunctions.StatisticsQueryValue(statistics, TC_COUNTER_INSTANCE_TOTAL_RX_PACKETS, &counter); if (status != TC_SUCCESS) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error in TcStatisticsQueryValue: %s (%08x)", g_TcFunctions.StatusGetString(status), status); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error in TcStatisticsQueryValue: %s (%08x)", g_TcFunctions.StatusGetString(status), status); return -1; } if (counter <= (ULONGLONG)0xFFFFFFFF) @@ -1077,7 +1024,7 @@ TcStats(pcap_t *p, struct pcap_stat *ps) status = g_TcFunctions.StatisticsQueryValue(statistics, TC_COUNTER_INSTANCE_RX_DROPPED_PACKETS, &counter); if (status != TC_SUCCESS) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error in TcStatisticsQueryValue: %s (%08x)", g_TcFunctions.StatusGetString(status), status); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error in TcStatisticsQueryValue: %s (%08x)", g_TcFunctions.StatusGetString(status), status); return -1; } if (counter <= (ULONGLONG)0xFFFFFFFF) @@ -1100,27 +1047,6 @@ TcStats(pcap_t *p, struct pcap_stat *ps) } -/* - * We filter at user level, since the kernel driver does't process the packets - */ -static int -TcSetFilter(pcap_t *p, struct bpf_program *fp) -{ - if(!fp) - { - strncpy(p->errbuf, "setfilter: No filter specified", sizeof(p->errbuf)); - return -1; - } - - /* Install a user level filter */ - if (install_bpf_program(p, fp) < 0) - { - return -1; - } - - return 0; -} - #ifdef _WIN32 static struct pcap_stat * TcStatsEx(pcap_t *p, int *pcap_stat_size) @@ -1136,7 +1062,7 @@ TcStatsEx(pcap_t *p, int *pcap_stat_size) if (status != TC_SUCCESS) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error in TcInstanceQueryStatistics: %s (%08x)", g_TcFunctions.StatusGetString(status), status); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error in TcInstanceQueryStatistics: %s (%08x)", g_TcFunctions.StatusGetString(status), status); return NULL; } @@ -1145,7 +1071,7 @@ TcStatsEx(pcap_t *p, int *pcap_stat_size) status = g_TcFunctions.StatisticsQueryValue(statistics, TC_COUNTER_INSTANCE_TOTAL_RX_PACKETS, &counter); if (status != TC_SUCCESS) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error in TcStatisticsQueryValue: %s (%08x)", g_TcFunctions.StatusGetString(status), status); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error in TcStatisticsQueryValue: %s (%08x)", g_TcFunctions.StatusGetString(status), status); return NULL; } if (counter <= (ULONGLONG)0xFFFFFFFF) @@ -1160,7 +1086,7 @@ TcStatsEx(pcap_t *p, int *pcap_stat_size) status = g_TcFunctions.StatisticsQueryValue(statistics, TC_COUNTER_INSTANCE_RX_DROPPED_PACKETS, &counter); if (status != TC_SUCCESS) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error in TcStatisticsQueryValue: %s (%08x)", g_TcFunctions.StatusGetString(status), status); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error in TcStatisticsQueryValue: %s (%08x)", g_TcFunctions.StatusGetString(status), status); return NULL; } if (counter <= (ULONGLONG)0xFFFFFFFF) @@ -1198,7 +1124,7 @@ TcSetMode(pcap_t *p, int mode) { if (mode != MODE_CAPT) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Mode %u not supported by TurboCap devices. TurboCap only supports capture.", mode); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Mode %d not supported by TurboCap devices. TurboCap only supports capture.", mode); return -1; } @@ -1213,7 +1139,7 @@ TcSetMinToCopy(pcap_t *p, int size) if (size < 0) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Mintocopy cannot be less than 0."); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Mintocopy cannot be less than 0."); return -1; } @@ -1221,7 +1147,7 @@ TcSetMinToCopy(pcap_t *p, int size) if (status != TC_SUCCESS) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error setting the mintocopy: %s (%08x)", g_TcFunctions.StatusGetString(status), status); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "TurboCap error setting the mintocopy: %s (%08x)", g_TcFunctions.StatusGetString(status), status); } return 0; @@ -1238,7 +1164,7 @@ TcGetReceiveWaitHandle(pcap_t *p) static int TcOidGetRequest(pcap_t *p, bpf_u_int32 oid _U_, void *data _U_, size_t *lenp _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "An OID get request cannot be performed on a TurboCap device"); return PCAP_ERROR; } @@ -1247,7 +1173,7 @@ static int TcOidSetRequest(pcap_t *p, bpf_u_int32 oid _U_, const void *data _U_, size_t *lenp _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "An OID set request cannot be performed on a TurboCap device"); return PCAP_ERROR; } @@ -1255,7 +1181,7 @@ TcOidSetRequest(pcap_t *p, bpf_u_int32 oid _U_, const void *data _U_, static u_int TcSendqueueTransmit(pcap_t *p, pcap_send_queue *queue _U_, int sync _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Packets cannot be bulk transmitted on a TurboCap device"); return 0; } @@ -1263,7 +1189,7 @@ TcSendqueueTransmit(pcap_t *p, pcap_send_queue *queue _U_, int sync _U_) static int TcSetUserBuffer(pcap_t *p, int size _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "The user buffer cannot be set on a TurboCap device"); return -1; } @@ -1271,7 +1197,7 @@ TcSetUserBuffer(pcap_t *p, int size _U_) static int TcLiveDump(pcap_t *p, char *filename _U_, int maxsize _U_, int maxpacks _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Live packet dumping cannot be performed on a TurboCap device"); return -1; } @@ -1279,7 +1205,7 @@ TcLiveDump(pcap_t *p, char *filename _U_, int maxsize _U_, int maxpacks _U_) static int TcLiveDumpEnded(pcap_t *p, int sync _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Live packet dumping cannot be performed on a TurboCap device"); return -1; } diff --git a/pcap-tstamp.manmisc.in b/pcap-tstamp.manmisc.in index 6437e80e64e7..eea8c1d29ee3 100644 --- a/pcap-tstamp.manmisc.in +++ b/pcap-tstamp.manmisc.in @@ -19,7 +19,7 @@ .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" -.TH PCAP-TSTAMP @MAN_MISC_INFO@ "8 March 2015" +.TH PCAP-TSTAMP @MAN_MISC_INFO@ "14 July 2020" .SH NAME pcap-tstamp \- packet time stamps in libpcap .SH DESCRIPTION @@ -58,7 +58,20 @@ single adjustment; different CPU cores on a multi-core or multi-processor system might be running at different speeds, or might not have time counters all synchronized, so packets time-stamped by different cores might not have -consistent time stamps. +consistent time stamps; +.IP +some time sources, such as those that supply POSIX "seconds since the +Epoch" time, do not count leap seconds, meaning that the seconds +portion +.RB ( tv_sec ) +of the time stamp might not be incremented for a leap second, so that +the fraction-of-a-second part of the time stamp might roll over past +zero but the second part would not change, or the clock might run +slightly more slowly for a period before the leap second. +.LP +For these reasons, time differences between packet time stamps will not +necessarily accurately reflect the time differences between the receipt +or transmission times of the packets. .LP In addition, packets time-stamped by different cores might be time-stamped in one order and added to the queue of packets for libpcap @@ -73,6 +86,9 @@ the host operating system. Those time stamps might not, however, be synchronized with the host operating system's clock, so that, for example, the time stamp of a packet might not correspond to the time stamp of an event on the host triggered by the arrival of that packet. +If they are synchronized with the host operating system's clock, some of +the issues listed above with time stamps supplied by the host operating +system may also apply to time stamps supplied by the capture device. .LP Depending on the capture device and the software on the host, libpcap might allow different types of time stamp to be used. The @@ -87,15 +103,15 @@ The list might be empty, in which case no choice of time stamp type is offered for that capture device. If the list is not empty, the .BR pcap_set_tstamp_type (3PCAP) routine can be used after a -.B pcap_create() +.BR pcap_create () call and before a -.B pcap_activate() +.BR pcap_activate () call to specify the type of time stamp to be used on the device. The time stamp types are listed here; the first value is the #define to use in code, the second value is the value returned by -.B pcap_tstamp_type_val_to_name(3PCAP) +.BR pcap_tstamp_type_val_to_name (3PCAP) and accepted by -.BR pcap_tstamp_type_name_to_val(3PCAP) . +.BR pcap_tstamp_type_name_to_val (3PCAP) . .RS 5 .TP 5 .BR PCAP_TSTAMP_HOST " - " host @@ -110,9 +126,14 @@ system's clock. .TP 5 .BR PCAP_TSTAMP_HOST_HIPREC " - " host_hiprec Time stamp provided by the host on which the capture is being done. -This is a high-precision time stamp; it might or might not be -synchronized with the host operating system's clock. It might be more -expensive to fetch than +This is a high-precision time stamp, synchronized with the host +operating system's clock. It might be more expensive to fetch than +.BR PCAP_TSTAMP_HOST_LOWPREC . +.TP 5 +.BR PCAP_TSTAMP_HOST_HIPREC_UNSYNCED " - " host_hiprec_unsynced +Time stamp provided by the host on which the capture is being done. +This is a high-precision time stamp, not synchronized with the host +operating system's clock. It might be more expensive to fetch than .BR PCAP_TSTAMP_HOST_LOWPREC . .TP 5 .BR PCAP_TSTAMP_ADAPTER " - " adapter @@ -126,6 +147,18 @@ done. This is a high-precision time stamp; it is not synchronized with the host operating system's clock. .RE .LP +Time stamps synchronized with the system clock can go backwards, as the +system clock can go backwards. If a clock is not in sync with the +system clock, that could be because the system clock isn't keeping +accurate time, because the other clock isn't keeping accurate time, or +both. +.LP +Host-provided time stamps generally correspond to the time when the +time-stamping code sees the packet; this could be some unknown amount of +time after the first or last bit of the packet is received by the +network adapter, due to batching of interrupts for packet arrival, +queueing delays, etc.. +.LP By default, when performing a live capture or reading from a savefile, time stamps are supplied as seconds since January 1, 1970, 00:00:00 UTC, and microseconds since that seconds value, even if higher-resolution @@ -137,15 +170,15 @@ discarded. The .BR pcap_set_tstamp_precision (3PCAP) routine can be used after a -.B pcap_create() +.BR pcap_create () call and after a -.B pcap_activate() +.BR pcap_activate () call to specify the resolution of the time stamps to get for the device. If the hardware or software cannot supply a higher-resolution time stamp, the -.B pcap_set_tstamp_precision() +.BR pcap_set_tstamp_precision () call will fail, and the time stamps supplied after the -.B pcap_activate() +.BR pcap_activate () call will have microsecond resolution. .LP When opening a savefile, the @@ -165,4 +198,4 @@ the time stamp supplied by the hardware or operating system and, when reading a savefile, this does not indicate the actual precision of time stamps in the file. .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap-types.h b/pcap-types.h index 9614f9f1de51..7d0fe81ff71a 100644 --- a/pcap-types.h +++ b/pcap-types.h @@ -35,7 +35,6 @@ * Get u_int defined, by hook or by crook. */ #ifdef _WIN32 - /* * This defines u_int. */ diff --git a/pcap-usb-linux-common.c b/pcap-usb-linux-common.c new file mode 100644 index 000000000000..fb4a8c19be75 --- /dev/null +++ b/pcap-usb-linux-common.c @@ -0,0 +1,130 @@ +/* + * Copyright (c) 1993, 1994, 1995, 1996, 1997 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * pcap-usb-linux-common.c - common code for everything that needs to + * deal with Linux USB captures. + */ + +#include "pcap/pcap.h" +#include "pcap/usb.h" + +#include "pcap-usb-linux-common.h" + +/* + * Compute, from the data provided by the Linux USB memory-mapped capture + * mechanism, the amount of packet data that would have been provided + * had the capture mechanism not chopped off any data at the end, if, in + * fact, it did so. + * + * Set the "unsliced length" field of the packet header to that value. + */ +void +fix_linux_usb_mmapped_length(struct pcap_pkthdr *pkth, const u_char *bp) +{ + const pcap_usb_header_mmapped *hdr; + u_int bytes_left; + + /* + * All callers of this routine must ensure that pkth->caplen is + * >= sizeof (pcap_usb_header_mmapped). + */ + bytes_left = pkth->caplen; + bytes_left -= sizeof (pcap_usb_header_mmapped); + + hdr = (const pcap_usb_header_mmapped *) bp; + if (!hdr->data_flag && hdr->transfer_type == URB_ISOCHRONOUS && + hdr->event_type == URB_COMPLETE && + (hdr->endpoint_number & URB_TRANSFER_IN) && + pkth->len == sizeof(pcap_usb_header_mmapped) + + (hdr->ndesc * sizeof (usb_isodesc)) + hdr->urb_len) { + usb_isodesc *descs; + u_int pre_truncation_data_len, pre_truncation_len; + + descs = (usb_isodesc *) (bp + sizeof(pcap_usb_header_mmapped)); + + /* + * We have data (yes, data_flag is 0 if we *do* have data), + * and this is a "this is complete" incoming isochronous + * transfer event, and the length was calculated based + * on the URB length. + * + * That's not correct, because the data isn't contiguous, + * and the isochronous descriptos show how it's scattered. + * + * Find the end of the last chunk of data in the buffer + * referred to by the isochronous descriptors; that indicates + * how far into the buffer the data would have gone. + * + * Make sure we don't run past the end of the captured data + * while processing the isochronous descriptors. + */ + pre_truncation_data_len = 0; + for (uint32_t desc = 0; + desc < hdr->ndesc && bytes_left >= sizeof (usb_isodesc); + desc++, bytes_left -= sizeof (usb_isodesc)) { + u_int desc_end; + + if (descs[desc].len != 0) { + desc_end = descs[desc].offset + descs[desc].len; + if (desc_end > pre_truncation_data_len) + pre_truncation_data_len = desc_end; + } + } + + /* + * Now calculate the total length based on that data + * length. + */ + pre_truncation_len = sizeof(pcap_usb_header_mmapped) + + (hdr->ndesc * sizeof (usb_isodesc)) + + pre_truncation_data_len; + + /* + * If that's greater than or equal to the captured length, + * use that as the length. + */ + if (pre_truncation_len >= pkth->caplen) + pkth->len = pre_truncation_len; + + /* + * If the captured length is greater than the length, + * use the captured length. + * + * For completion events for incoming isochronous transfers, + * it's based on data_len, which is calculated the same way + * we calculated pre_truncation_data_len above, except that + * it has access to all the isochronous descriptors, not + * just the ones that the kernel were able to provide us or, + * for a capture file, that weren't sliced off by a snapshot + * length. + * + * However, it might have been reduced by the USB capture + * mechanism arbitrarily limiting the amount of data it + * provides to userland, or by the libpcap capture code + * limiting it to being no more than the snapshot, so + * we don't want to just use it all the time; we only + * do so to try to get a better estimate of the actual + * length - and to make sure the on-the-network length + * is always >= the captured length. + */ + if (pkth->caplen > pkth->len) + pkth->len = pkth->caplen; + } +} diff --git a/pcap-usb-linux-common.h b/pcap-usb-linux-common.h new file mode 100644 index 000000000000..8cff7ba1cf6b --- /dev/null +++ b/pcap-usb-linux-common.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 1993, 1994, 1995, 1996, 1997 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * pcap-usb-linux-common.h - common code for everything that needs to + * deal with Linux USB captures. + */ + +extern void fix_linux_usb_mmapped_length(struct pcap_pkthdr *pkth, + const u_char *bp); diff --git a/pcap-usb-linux.c b/pcap-usb-linux.c index b306dca9b951..7020577ca6cb 100644 --- a/pcap-usb-linux.c +++ b/pcap-usb-linux.c @@ -39,13 +39,15 @@ #include "pcap-int.h" #include "pcap-usb-linux.h" +#include "pcap-usb-linux-common.h" #include "pcap/usb.h" +#include "extract.h" + #ifdef NEED_STRERROR_H #include "strerror.h" #endif -#include #include #include #include @@ -69,11 +71,12 @@ #include #endif /* HAVE_LINUX_USBDEVICE_FS_H */ +#include "diag-control.h" + #define USB_IFACE "usbmon" -#define USB_TEXT_DIR_OLD "/sys/kernel/debug/usbmon" -#define USB_TEXT_DIR "/sys/kernel/debug/usb/usbmon" -#define SYS_USB_BUS_DIR "/sys/bus/usb/devices" -#define PROC_USB_BUS_DIR "/proc/bus/usb" + +#define USBMON_DEV_PREFIX "usbmon" +#define USBMON_DEV_PREFIX_LEN (sizeof USBMON_DEV_PREFIX - 1) #define USB_LINE_LEN 4096 #if __BYTE_ORDER == __LITTLE_ENDIAN @@ -131,106 +134,20 @@ struct pcap_usb_linux { /* forward declaration */ static int usb_activate(pcap_t *); -static int usb_stats_linux(pcap_t *, struct pcap_stat *); static int usb_stats_linux_bin(pcap_t *, struct pcap_stat *); -static int usb_read_linux(pcap_t *, int , pcap_handler , u_char *); static int usb_read_linux_bin(pcap_t *, int , pcap_handler , u_char *); static int usb_read_linux_mmap(pcap_t *, int , pcap_handler , u_char *); -static int usb_inject_linux(pcap_t *, const void *, size_t); +static int usb_inject_linux(pcap_t *, const void *, int); static int usb_setdirection_linux(pcap_t *, pcap_direction_t); static void usb_cleanup_linux_mmap(pcap_t *); -static int -have_binary_usbmon(void) -{ - struct utsname utsname; - char *version_component, *endp; - int major, minor, subminor; - - if (uname(&utsname) == 0) { - /* - * 2.6.21 is the first release with the binary-mode - * USB monitoring. - */ - version_component = utsname.release; - major = strtol(version_component, &endp, 10); - if (endp != version_component && *endp == '.') { - /* - * OK, that was a valid major version. - * Is it 3 or greater? If so, we have binary - * mode support. - */ - if (major >= 3) - return 1; - - /* - * Is it 1 or less? If so, we don't have binary - * mode support. (In fact, we don't have any - * USB monitoring....) - */ - if (major <= 1) - return 0; - } - - /* - * OK, this is a 2.x kernel. - * What's the minor version? - */ - version_component = endp + 1; - minor = strtol(version_component, &endp, 10); - if (endp != version_component && - (*endp == '.' || *endp == '\0')) { - /* - * OK, that was a valid minor version. - * Is is 2.6 or later? (There shouldn't be a - * "later", as 2.6.x went to 3.x, but we'll - * check anyway.) - */ - if (minor < 6) { - /* - * No, so no binary support (did 2.4 have - * any USB monitoring at all?) - */ - return 0; - } - - /* - * OK, this is a 2.6.x kernel. - * What's the subminor version? - */ - version_component = endp + 1; - subminor = strtol(version_component, &endp, 10); - if (endp != version_component && - (*endp == '.' || *endp == '\0')) { - /* - * OK, that was a valid subminor version. - * Is it 21 or greater? - */ - if (subminor >= 21) { - /* - * Yes - we have binary mode - * support. - */ - return 1; - } - } - } - } - - /* - * Either uname() failed, in which case we just say "no binary - * mode support", or we don't have binary mode support. - */ - return 0; -} - /* facility to add an USB device to the device list*/ static int usb_dev_add(pcap_if_list_t *devlistp, int n, char *err_str) { char dev_name[10]; char dev_descr[30]; - pcap_snprintf(dev_name, 10, USB_IFACE"%d", n); + snprintf(dev_name, 10, USB_IFACE"%d", n); /* * XXX - is there any notion of "up" and "running"? */ @@ -251,7 +168,7 @@ usb_dev_add(pcap_if_list_t *devlistp, int n, char *err_str) * PCAP_IF_CONNECTION_STATUS_CONNECTED or * PCAP_IF_CONNECTION_STATUS_DISCONNECTED? */ - pcap_snprintf(dev_descr, 30, "Raw USB traffic, bus number %d", n); + snprintf(dev_descr, 30, "Raw USB traffic, bus number %d", n); if (add_dev(devlistp, dev_name, 0, dev_descr, err_str) == NULL) return -1; } @@ -262,123 +179,41 @@ usb_dev_add(pcap_if_list_t *devlistp, int n, char *err_str) int usb_findalldevs(pcap_if_list_t *devlistp, char *err_str) { - char usb_mon_dir[PATH_MAX]; - char *usb_mon_prefix; - size_t usb_mon_prefix_len; struct dirent* data; int ret = 0; DIR* dir; int n; char* name; - size_t len; - if (have_binary_usbmon()) { - /* - * We have binary-mode support. - * What do the device names look like? - * Split LINUX_USB_MON_DEV into a directory that we'll - * scan and a file name prefix that we'll check for. - */ - pcap_strlcpy(usb_mon_dir, LINUX_USB_MON_DEV, sizeof usb_mon_dir); - usb_mon_prefix = strrchr(usb_mon_dir, '/'); - if (usb_mon_prefix == NULL) { + /* + * We require 2.6.27 or later kernels, so we have binary-mode support. + * The devices are of the form /dev/usbmon{N}. + * Open /dev and scan it. + */ + dir = opendir("/dev"); + if (dir != NULL) { + while ((ret == 0) && ((data = readdir(dir)) != 0)) { + name = data->d_name; + /* - * This "shouldn't happen". Just give up if it - * does. + * Is this a usbmon device? */ - return 0; - } - *usb_mon_prefix++ = '\0'; - usb_mon_prefix_len = strlen(usb_mon_prefix); + if (strncmp(name, USBMON_DEV_PREFIX, + USBMON_DEV_PREFIX_LEN) != 0) + continue; /* no */ - /* - * Open the directory and scan it. - */ - dir = opendir(usb_mon_dir); - if (dir != NULL) { - while ((ret == 0) && ((data = readdir(dir)) != 0)) { - name = data->d_name; + /* + * What's the device number? + */ + if (sscanf(&name[USBMON_DEV_PREFIX_LEN], "%d", &n) == 0) + continue; /* failed */ - /* - * Is this a usbmon device? - */ - if (strncmp(name, usb_mon_prefix, usb_mon_prefix_len) != 0) - continue; /* no */ - - /* - * What's the device number? - */ - if (sscanf(&name[usb_mon_prefix_len], "%d", &n) == 0) - continue; /* failed */ - - ret = usb_dev_add(devlistp, n, err_str); - } - - closedir(dir); - } - return 0; - } else { - /* - * We have only text mode support. - * We don't look for the text devices because we can't - * look for them without root privileges, and we don't - * want to require root privileges to enumerate devices - * (we want to let the user to try a device and get - * an error, rather than seeing no devices and asking - * "why am I not seeing devices" and forcing a long - * process of poking to figure out whether it's "no - * privileges" or "your kernel is too old" or "the - * usbmon module isn't loaded" or...). - * - * Instead, we look to see what buses we have. - * If the kernel is so old that it doesn't have - * binary-mode support, it's also so old that - * it doesn't have a "scan all buses" device. - * - * First, try scanning sysfs USB bus directory. - */ - dir = opendir(SYS_USB_BUS_DIR); - if (dir != NULL) { - while ((ret == 0) && ((data = readdir(dir)) != 0)) { - name = data->d_name; - - if (strncmp(name, "usb", 3) != 0) - continue; - - if (sscanf(&name[3], "%d", &n) == 0) - continue; - - ret = usb_dev_add(devlistp, n, err_str); - } - - closedir(dir); - return 0; + ret = usb_dev_add(devlistp, n, err_str); } - /* That didn't work; try scanning procfs USB bus directory. */ - dir = opendir(PROC_USB_BUS_DIR); - if (dir != NULL) { - while ((ret == 0) && ((data = readdir(dir)) != 0)) { - name = data->d_name; - len = strlen(name); - - /* if this file name does not end with a number it's not of our interest */ - if ((len < 1) || !isdigit(name[--len])) - continue; - while (isdigit(name[--len])); - if (sscanf(&name[len+1], "%d", &n) != 1) - continue; - - ret = usb_dev_add(devlistp, n, err_str); - } - - closedir(dir); - return ret; - } - - /* neither of them worked */ - return 0; + closedir(dir); } + return 0; } /* @@ -490,6 +325,10 @@ int usb_mmap(pcap_t* handle) #define USB_REQ_GET_DESCRIPTOR 6 #define USB_DT_DEVICE 1 +#define USB_DT_CONFIG 2 + +#define USB_DEVICE_DESCRIPTOR_SIZE 18 +#define USB_CONFIG_DESCRIPTOR_SIZE 9 /* probe the descriptors of the devices attached to the bus */ /* the descriptors will end up in the captured packet stream */ @@ -501,12 +340,14 @@ probe_devices(int bus) struct usbdevfs_ctrltransfer ctrl; struct dirent* data; int ret = 0; - char buf[sizeof("/dev/bus/usb/000/") + NAME_MAX]; + char busdevpath[sizeof("/dev/bus/usb/000/") + NAME_MAX]; DIR* dir; + uint8_t descriptor[USB_DEVICE_DESCRIPTOR_SIZE]; + uint8_t configdesc[USB_CONFIG_DESCRIPTOR_SIZE]; /* scan usb bus directories for device nodes */ - pcap_snprintf(buf, sizeof(buf), "/dev/bus/usb/%03d", bus); - dir = opendir(buf); + snprintf(busdevpath, sizeof(busdevpath), "/dev/bus/usb/%03d", bus); + dir = opendir(busdevpath); if (!dir) return; @@ -517,9 +358,9 @@ probe_devices(int bus) if (name[0] == '.') continue; - pcap_snprintf(buf, sizeof(buf), "/dev/bus/usb/%03d/%s", bus, data->d_name); + snprintf(busdevpath, sizeof(busdevpath), "/dev/bus/usb/%03d/%s", bus, data->d_name); - fd = open(buf, O_RDWR); + fd = open(busdevpath, O_RDWR); if (fd == -1) continue; @@ -532,19 +373,43 @@ probe_devices(int bus) ctrl.bRequest = USB_REQ_GET_DESCRIPTOR; ctrl.wValue = USB_DT_DEVICE << 8; ctrl.wIndex = 0; - ctrl.wLength = sizeof(buf); + ctrl.wLength = sizeof(descriptor); #else ctrl.requesttype = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE; ctrl.request = USB_REQ_GET_DESCRIPTOR; ctrl.value = USB_DT_DEVICE << 8; ctrl.index = 0; - ctrl.length = sizeof(buf); + ctrl.length = sizeof(descriptor); #endif - ctrl.data = buf; + ctrl.data = descriptor; ctrl.timeout = CTRL_TIMEOUT; ret = ioctl(fd, USBDEVFS_CONTROL, &ctrl); + /* Request CONFIGURATION descriptor alone to know wTotalLength */ +#ifdef HAVE_STRUCT_USBDEVFS_CTRLTRANSFER_BREQUESTTYPE + ctrl.wValue = USB_DT_CONFIG << 8; + ctrl.wLength = sizeof(configdesc); +#else + ctrl.value = USB_DT_CONFIG << 8; + ctrl.length = sizeof(configdesc); +#endif + ctrl.data = configdesc; + ret = ioctl(fd, USBDEVFS_CONTROL, &ctrl); + if (ret >= 0) { + uint16_t wtotallength; + wtotallength = EXTRACT_LE_U_2(&configdesc[2]); +#ifdef HAVE_STRUCT_USBDEVFS_CTRLTRANSFER_BREQUESTTYPE + ctrl.wLength = wtotallength; +#else + ctrl.length = wtotallength; +#endif + ctrl.data = malloc(wtotallength); + if (ctrl.data) { + ret = ioctl(fd, USBDEVFS_CONTROL, &ctrl); + free(ctrl.data); + } + } close(fd); } closedir(dir); @@ -586,7 +451,7 @@ usb_create(const char *device, char *ebuf, int *is_ours) /* OK, it's probably ours. */ *is_ours = 1; - p = pcap_create_common(ebuf, sizeof (struct pcap_usb_linux)); + p = PCAP_CREATE_COMMON(ebuf, struct pcap_usb_linux); if (p == NULL) return (NULL); @@ -599,7 +464,6 @@ usb_activate(pcap_t* handle) { struct pcap_usb_linux *handlep = handle->priv; char full_path[USB_LINE_LEN]; - int ret; /* * Turn a negative snapshot value (invalid), a snapshot value of @@ -627,172 +491,114 @@ usb_activate(pcap_t* handle) /*get usb bus index from device name */ if (sscanf(handle->opt.device, USB_IFACE"%d", &handlep->bus_index) != 1) { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't get USB bus index from %s", handle->opt.device); return PCAP_ERROR; } - if (have_binary_usbmon()) + /* + * We require 2.6.27 or later kernels, so we have binary-mode support. + * Try to open the binary interface. + */ + snprintf(full_path, USB_LINE_LEN, "/dev/"USBMON_DEV_PREFIX"%d", + handlep->bus_index); + handle->fd = open(full_path, O_RDONLY, 0); + if (handle->fd < 0) { /* - * We have binary-mode support. - * Try to open the binary interface. + * The attempt failed; why? */ - pcap_snprintf(full_path, USB_LINE_LEN, LINUX_USB_MON_DEV"%d", handlep->bus_index); - handle->fd = open(full_path, O_RDONLY, 0); - if (handle->fd < 0) - { + switch (errno) { + + case ENOENT: /* - * The attempt failed; why? + * The device doesn't exist. + * That could either mean that there's + * no support for monitoring USB buses + * (which probably means "the usbmon + * module isn't loaded") or that there + * is but that *particular* device + * doesn't exist (no "scan all buses" + * device if the bus index is 0, no + * such bus if the bus index isn't 0). + * + * For now, don't provide an error message; + * if we can determine what the particular + * problem is, we should report that. */ - switch (errno) { + handle->errbuf[0] = '\0'; + return PCAP_ERROR_NO_SUCH_DEVICE; - case ENOENT: - /* - * The device doesn't exist. - * That could either mean that there's - * no support for monitoring USB buses - * (which probably means "the usbmon - * module isn't loaded") or that there - * is but that *particular* device - * doesn't exist (no "scan all buses" - * device if the bus index is 0, no - * such bus if the bus index isn't 0). - */ - return PCAP_ERROR_NO_SUCH_DEVICE; - - case EACCES: - /* - * We didn't have permission to open it. - */ - return PCAP_ERROR_PERM_DENIED; - - default: - /* - * Something went wrong. - */ - pcap_fmt_errmsg_for_errno(handle->errbuf, - PCAP_ERRBUF_SIZE, errno, - "Can't open USB bus file %s", full_path); - return PCAP_ERROR; - } - } - - if (handle->opt.rfmon) - { + case EACCES: /* - * Monitor mode doesn't apply to USB devices. + * We didn't have permission to open it. */ - close(handle->fd); - return PCAP_ERROR_RFMON_NOTSUP; - } - - /* try to use fast mmap access */ - if (usb_mmap(handle)) - { - /* We succeeded. */ - handle->linktype = DLT_USB_LINUX_MMAPPED; - handle->stats_op = usb_stats_linux_bin; - handle->read_op = usb_read_linux_mmap; - handle->cleanup_op = usb_cleanup_linux_mmap; -#ifdef HAVE_LINUX_USBDEVICE_FS_H - probe_devices(handlep->bus_index); -#endif +DIAG_OFF_FORMAT_TRUNCATION + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "Attempt to open %s failed with EACCES - root privileges may be required", + full_path); +DIAG_ON_FORMAT_TRUNCATION + return PCAP_ERROR_PERM_DENIED; + default: /* - * "handle->fd" is a real file, so - * "select()" and "poll()" work on it. + * Something went wrong. */ - handle->selectable_fd = handle->fd; - return 0; - } - - /* - * We failed; try plain binary interface access. - * - * Attempt to set the ring size as appropriate for - * the snapshot length, reducing the snapshot length - * if that'd make the ring bigger than the kernel - * supports. - */ - if (usb_set_ring_size(handle, (int)sizeof(pcap_usb_header)) == -1) { - /* Failed. */ - close(handle->fd); + pcap_fmt_errmsg_for_errno(handle->errbuf, + PCAP_ERRBUF_SIZE, errno, + "Can't open USB bus file %s", full_path); return PCAP_ERROR; } + } + + if (handle->opt.rfmon) + { + /* + * Monitor mode doesn't apply to USB devices. + */ + close(handle->fd); + return PCAP_ERROR_RFMON_NOTSUP; + } + + /* try to use fast mmap access */ + if (usb_mmap(handle)) + { + /* We succeeded. */ + handle->linktype = DLT_USB_LINUX_MMAPPED; handle->stats_op = usb_stats_linux_bin; - handle->read_op = usb_read_linux_bin; + handle->read_op = usb_read_linux_mmap; + handle->cleanup_op = usb_cleanup_linux_mmap; #ifdef HAVE_LINUX_USBDEVICE_FS_H probe_devices(handlep->bus_index); #endif - } - else { + /* - * We don't have binary mode support. - * Try opening the text-mode device. + * "handle->fd" is a real file, so + * "select()" and "poll()" work on it. */ - pcap_snprintf(full_path, USB_LINE_LEN, USB_TEXT_DIR"/%dt", handlep->bus_index); - handle->fd = open(full_path, O_RDONLY, 0); - if (handle->fd < 0) - { - if (errno == ENOENT) - { - /* - * Not found at the new location; try - * the old location. - */ - pcap_snprintf(full_path, USB_LINE_LEN, USB_TEXT_DIR_OLD"/%dt", handlep->bus_index); - handle->fd = open(full_path, O_RDONLY, 0); - } - if (handle->fd < 0) { - if (errno == ENOENT) - { - /* - * The problem is that the file - * doesn't exist. Report that as - * "no such device". (That could - * mean "no such USB bus" or - * "monitoring not supported".) - */ - ret = PCAP_ERROR_NO_SUCH_DEVICE; - } - else if (errno == EACCES) - { - /* - * The problem is that we don't - * have sufficient permission to - * open the file. Report that. - */ - ret = PCAP_ERROR_PERM_DENIED; - } - else - { - /* - * Some other error. - */ - ret = PCAP_ERROR; - } - pcap_fmt_errmsg_for_errno(handle->errbuf, - PCAP_ERRBUF_SIZE, errno, - "Can't open USB bus file %s", - full_path); - return ret; - } - } - - if (handle->opt.rfmon) - { - /* - * Monitor mode doesn't apply to USB devices. - */ - close(handle->fd); - return PCAP_ERROR_RFMON_NOTSUP; - } - - handle->stats_op = usb_stats_linux; - handle->read_op = usb_read_linux; + handle->selectable_fd = handle->fd; + return 0; } + /* + * We failed; try plain binary interface access. + * + * Attempt to set the ring size as appropriate for + * the snapshot length, reducing the snapshot length + * if that'd make the ring bigger than the kernel + * supports. + */ + if (usb_set_ring_size(handle, (int)sizeof(pcap_usb_header)) == -1) { + /* Failed. */ + close(handle->fd); + return PCAP_ERROR; + } + handle->stats_op = usb_stats_linux_bin; + handle->read_op = usb_read_linux_bin; +#ifdef HAVE_LINUX_USBDEVICE_FS_H + probe_devices(handlep->bus_index); +#endif + /* * "handle->fd" is a real file, so "select()" and "poll()" * work on it. @@ -811,308 +617,25 @@ usb_activate(pcap_t* handle) return 0; } -static inline int -ascii_to_int(char c) -{ - return c < 'A' ? c- '0': ((c<'a') ? c - 'A' + 10: c-'a'+10); -} - -/* - * see /Documentation/usb/usbmon.txt and - * /drivers/usb/mon/mon_text.c for urb string - * format description - */ static int -usb_read_linux(pcap_t *handle, int max_packets _U_, pcap_handler callback, u_char *user) +usb_inject_linux(pcap_t *handle, const void *buf _U_, int size _U_) { - /* see: - * /usr/src/linux/Documentation/usb/usbmon.txt - * for message format - */ - struct pcap_usb_linux *handlep = handle->priv; - unsigned timestamp; - int tag, cnt, ep_num, dev_addr, dummy, ret, urb_len, data_len; - char etype, pipeid1, pipeid2, status[16], urb_tag, line[USB_LINE_LEN]; - char *string = line; - u_char * rawdata = handle->buffer; - struct pcap_pkthdr pkth; - pcap_usb_header* uhdr = (pcap_usb_header*)handle->buffer; - u_char urb_transfer=0; - int incoming=0; - - /* ignore interrupt system call errors */ - do { - ret = read(handle->fd, line, USB_LINE_LEN - 1); - if (handle->break_loop) - { - handle->break_loop = 0; - return -2; - } - } while ((ret == -1) && (errno == EINTR)); - if (ret < 0) - { - if (errno == EAGAIN) - return 0; /* no data there */ - - pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, - errno, "Can't read from fd %d", handle->fd); - return -1; - } - - /* read urb header; %n argument may increment return value, but it's - * not mandatory, so does not count on it*/ - string[ret] = 0; - ret = sscanf(string, "%x %d %c %c%c:%d:%d %s%n", &tag, ×tamp, &etype, - &pipeid1, &pipeid2, &dev_addr, &ep_num, status, - &cnt); - if (ret < 8) - { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, - "Can't parse USB bus message '%s', too few tokens (expected 8 got %d)", - string, ret); - return -1; - } - uhdr->id = tag; - uhdr->device_address = dev_addr; - uhdr->bus_id = handlep->bus_index; - uhdr->status = 0; - string += cnt; - - /* don't use usbmon provided timestamp, since it have low precision*/ - if (gettimeofday(&pkth.ts, NULL) < 0) - { - pcap_fmt_errmsg_for_errno(handle->errbuf, PCAP_ERRBUF_SIZE, - errno, "Can't get timestamp for message '%s'", string); - return -1; - } - uhdr->ts_sec = pkth.ts.tv_sec; - uhdr->ts_usec = pkth.ts.tv_usec; - - /* parse endpoint information */ - if (pipeid1 == 'C') - urb_transfer = URB_CONTROL; - else if (pipeid1 == 'Z') - urb_transfer = URB_ISOCHRONOUS; - else if (pipeid1 == 'I') - urb_transfer = URB_INTERRUPT; - else if (pipeid1 == 'B') - urb_transfer = URB_BULK; - if (pipeid2 == 'i') { - ep_num |= URB_TRANSFER_IN; - incoming = 1; - } - if (etype == 'C') - incoming = !incoming; - - /* direction check*/ - if (incoming) - { - if (handle->direction == PCAP_D_OUT) - return 0; - } - else - if (handle->direction == PCAP_D_IN) - return 0; - uhdr->event_type = etype; - uhdr->transfer_type = urb_transfer; - uhdr->endpoint_number = ep_num; - pkth.caplen = sizeof(pcap_usb_header); - rawdata += sizeof(pcap_usb_header); - - /* check if this is a setup packet */ - ret = sscanf(status, "%d", &dummy); - if (ret != 1) - { - /* this a setup packet, setup data can be filled with underscore if - * usbmon has not been able to read them, so we must parse this fields as - * strings */ - pcap_usb_setup* shdr; - char str1[3], str2[3], str3[5], str4[5], str5[5]; - ret = sscanf(string, "%s %s %s %s %s%n", str1, str2, str3, str4, - str5, &cnt); - if (ret < 5) - { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, - "Can't parse USB bus message '%s', too few tokens (expected 5 got %d)", - string, ret); - return -1; - } - string += cnt; - - /* try to convert to corresponding integer */ - shdr = &uhdr->setup; - shdr->bmRequestType = strtoul(str1, 0, 16); - shdr->bRequest = strtoul(str2, 0, 16); - shdr->wValue = htols(strtoul(str3, 0, 16)); - shdr->wIndex = htols(strtoul(str4, 0, 16)); - shdr->wLength = htols(strtoul(str5, 0, 16)); - - uhdr->setup_flag = 0; - } - else - uhdr->setup_flag = 1; - - /* read urb data */ - ret = sscanf(string, " %d%n", &urb_len, &cnt); - if (ret < 1) - { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, - "Can't parse urb length from '%s'", string); - return -1; - } - string += cnt; - - /* urb tag is not present if urb length is 0, so we can stop here - * text parsing */ - pkth.len = urb_len+pkth.caplen; - uhdr->urb_len = urb_len; - uhdr->data_flag = 1; - data_len = 0; - if (uhdr->urb_len == 0) - goto got; - - /* check for data presence; data is present if and only if urb tag is '=' */ - if (sscanf(string, " %c", &urb_tag) != 1) - { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, - "Can't parse urb tag from '%s'", string); - return -1; - } - - if (urb_tag != '=') - goto got; - - /* skip urb tag and following space */ - string += 3; - - /* if we reach this point we got some urb data*/ - uhdr->data_flag = 0; - - /* read all urb data; if urb length is greater then the usbmon internal - * buffer length used by the kernel to spool the URB, we get only - * a partial information. - * At least until linux 2.6.17 there is no way to set usbmon intenal buffer - * length and default value is 130. */ - while ((string[0] != 0) && (string[1] != 0) && (pkth.caplen < (bpf_u_int32)handle->snapshot)) - { - rawdata[0] = ascii_to_int(string[0]) * 16 + ascii_to_int(string[1]); - rawdata++; - string+=2; - if (string[0] == ' ') - string++; - pkth.caplen++; - data_len++; - } - -got: - uhdr->data_len = data_len; - if (pkth.caplen > (bpf_u_int32)handle->snapshot) - pkth.caplen = (bpf_u_int32)handle->snapshot; - - if (handle->fcode.bf_insns == NULL || - bpf_filter(handle->fcode.bf_insns, handle->buffer, - pkth.len, pkth.caplen)) { - handlep->packets_read++; - callback(user, &pkth, handle->buffer); - return 1; - } - return 0; /* didn't pass filter */ -} - -static int -usb_inject_linux(pcap_t *handle, const void *buf _U_, size_t size _U_) -{ - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Packet injection is not supported on USB devices"); return (-1); } -static int -usb_stats_linux(pcap_t *handle, struct pcap_stat *stats) -{ - struct pcap_usb_linux *handlep = handle->priv; - int dummy, ret, consumed, cnt; - char string[USB_LINE_LEN]; - char token[USB_LINE_LEN]; - char * ptr = string; - int fd; - - pcap_snprintf(string, USB_LINE_LEN, USB_TEXT_DIR"/%ds", handlep->bus_index); - fd = open(string, O_RDONLY, 0); - if (fd < 0) - { - if (errno == ENOENT) - { - /* - * Not found at the new location; try the old - * location. - */ - pcap_snprintf(string, USB_LINE_LEN, USB_TEXT_DIR_OLD"/%ds", handlep->bus_index); - fd = open(string, O_RDONLY, 0); - } - if (fd < 0) { - pcap_fmt_errmsg_for_errno(handle->errbuf, - PCAP_ERRBUF_SIZE, errno, - "Can't open USB stats file %s", string); - return -1; - } - } - - /* read stats line */ - do { - ret = read(fd, string, USB_LINE_LEN-1); - } while ((ret == -1) && (errno == EINTR)); - close(fd); - - if (ret < 0) - { - pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, - "Can't read stats from fd %d ", fd); - return -1; - } - string[ret] = 0; - - stats->ps_recv = handlep->packets_read; - stats->ps_drop = 0; /* unless we find text_lost */ - stats->ps_ifdrop = 0; - - /* extract info on dropped urbs */ - for (consumed=0; consumed < ret; ) { - /* from the sscanf man page: - * The C standard says: "Execution of a %n directive does - * not increment the assignment count returned at the completion - * of execution" but the Corrigendum seems to contradict this. - * Do not make any assumptions on the effect of %n conversions - * on the return value and explicitly check for cnt assignmet*/ - int ntok; - - cnt = -1; - ntok = sscanf(ptr, "%s%n", token, &cnt); - if ((ntok < 1) || (cnt < 0)) - break; - consumed += cnt; - ptr += cnt; - if (strcmp(token, "text_lost") == 0) - ntok = sscanf(ptr, "%d%n", &stats->ps_drop, &cnt); - else - ntok = sscanf(ptr, "%d%n", &dummy, &cnt); - if ((ntok != 1) || (cnt < 0)) - break; - consumed += cnt; - ptr += cnt; - } - - return 0; -} - static int usb_setdirection_linux(pcap_t *p, pcap_direction_t d) { + /* + * It's guaranteed, at this point, that d is a valid + * direction value. + */ p->direction = d; return 0; } - static int usb_stats_linux_bin(pcap_t *handle, struct pcap_stat *stats) { @@ -1208,11 +731,11 @@ usb_read_linux_bin(pcap_t *handle, int max_packets _U_, pcap_handler callback, u */ pkth.len = sizeof(pcap_usb_header) + info.hdr->urb_len; } - pkth.ts.tv_sec = info.hdr->ts_sec; + pkth.ts.tv_sec = (time_t)info.hdr->ts_sec; pkth.ts.tv_usec = info.hdr->ts_usec; if (handle->fcode.bf_insns == NULL || - bpf_filter(handle->fcode.bf_insns, handle->buffer, + pcap_filter(handle->fcode.bf_insns, handle->buffer, pkth.len, pkth.caplen)) { handlep->packets_read++; callback(user, &pkth, handle->buffer); @@ -1234,6 +757,7 @@ usb_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback, u_ch struct mon_bin_mfetch fetch; int32_t vec[VEC_SIZE]; struct pcap_pkthdr pkth; + u_char *bp; pcap_usb_header_mmapped* hdr; int nflush = 0; int packets = 0; @@ -1243,13 +767,40 @@ usb_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback, u_ch for (;;) { int i, ret; - int limit = max_packets - packets; - if (limit <= 0) - limit = VEC_SIZE; - if (limit > VEC_SIZE) - limit = VEC_SIZE; + int limit; - /* try to fetch as many events as possible*/ + if (PACKET_COUNT_IS_UNLIMITED(max_packets)) { + /* + * There's no limit on the number of packets + * to process, so try to fetch VEC_SIZE packets. + */ + limit = VEC_SIZE; + } else { + /* + * Try to fetch as many packets as we have left + * to process, or VEC_SIZE packets, whichever + * is less. + * + * At this point, max_packets > 0 (otherwise, + * PACKET_COUNT_IS_UNLIMITED(max_packets) + * would be true) and max_packets > packets + * (packet starts out as 0, and the test + * at the bottom of the loop exits if + * max_packets <= packets), so limit is + * guaranteed to be > 0. + */ + limit = max_packets - packets; + if (limit > VEC_SIZE) + limit = VEC_SIZE; + } + + /* + * Try to fetch as many events as possible, up to + * the limit, and flush the events we've processed + * earlier (nflush) - MON_IOCX_MFETCH does both + * (presumably to reduce the number of system + * calls in loops like this). + */ fetch.offvec = vec; fetch.nfetch = limit; fetch.nflush = nflush; @@ -1276,8 +827,27 @@ usb_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback, u_ch /* keep track of processed events, we will flush them later */ nflush = fetch.nfetch; for (i=0; immapbuf[vec[i]]; + + /* That begins with a metadata header */ + hdr = (pcap_usb_header_mmapped*) bp; + /* discard filler */ - hdr = (pcap_usb_header_mmapped*) &handlep->mmapbuf[vec[i]]; if (hdr->event_type == '@') continue; @@ -1322,12 +892,19 @@ usb_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback, u_ch */ pkth.len = sizeof(pcap_usb_header_mmapped) + (hdr->ndesc * sizeof (usb_isodesc)) + hdr->urb_len; + + /* + * Now clean it up if it's a completion + * event for an incoming isochronous + * transfer. + */ + fix_linux_usb_mmapped_length(&pkth, bp); } - pkth.ts.tv_sec = hdr->ts_sec; + pkth.ts.tv_sec = (time_t)hdr->ts_sec; pkth.ts.tv_usec = hdr->ts_usec; if (handle->fcode.bf_insns == NULL || - bpf_filter(handle->fcode.bf_insns, (u_char*) hdr, + pcap_filter(handle->fcode.bf_insns, (u_char*) hdr, pkth.len, pkth.caplen)) { handlep->packets_read++; callback(user, &pkth, (u_char*) hdr); @@ -1335,8 +912,12 @@ usb_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback, u_ch } } - /* with max_packets specifying "unlimited" we stop afer the first chunk*/ - if (PACKET_COUNT_IS_UNLIMITED(max_packets) || (packets == max_packets)) + /* + * If max_packets specifiesg "unlimited", we stop after + * the first chunk. + */ + if (PACKET_COUNT_IS_UNLIMITED(max_packets) || + (packets >= max_packets)) break; } diff --git a/pcap-util.c b/pcap-util.c new file mode 100644 index 000000000000..8b5669e90b9a --- /dev/null +++ b/pcap-util.c @@ -0,0 +1,474 @@ +/* + * Copyright (c) 1993, 1994, 1995, 1996, 1997 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * pcap-common.c - common code for pcap and pcapng files + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "pcap-int.h" +#include "extract.h" +#include "pcap-usb-linux-common.h" + +#include "pcap-util.h" + +#include "pflog.h" +#include "pcap/can_socketcan.h" +#include "pcap/sll.h" +#include "pcap/usb.h" +#include "pcap/nflog.h" + +/* + * Most versions of the DLT_PFLOG pseudo-header have UID and PID fields + * that are saved in host byte order. + * + * When reading a DLT_PFLOG packet, we need to convert those fields from + * the byte order of the host that wrote the file to this host's byte + * order. + */ +static void +swap_pflog_header(const struct pcap_pkthdr *hdr, u_char *buf) +{ + u_int caplen = hdr->caplen; + u_int length = hdr->len; + u_int pfloghdr_length; + struct pfloghdr *pflhdr = (struct pfloghdr *)buf; + + if (caplen < (u_int) (offsetof(struct pfloghdr, uid) + sizeof pflhdr->uid) || + length < (u_int) (offsetof(struct pfloghdr, uid) + sizeof pflhdr->uid)) { + /* Not enough data to have the uid field */ + return; + } + + pfloghdr_length = pflhdr->length; + + if (pfloghdr_length < (u_int) (offsetof(struct pfloghdr, uid) + sizeof pflhdr->uid)) { + /* Header doesn't include uid field */ + return; + } + pflhdr->uid = SWAPLONG(pflhdr->uid); + + if (caplen < (u_int) (offsetof(struct pfloghdr, pid) + sizeof pflhdr->pid) || + length < (u_int) (offsetof(struct pfloghdr, pid) + sizeof pflhdr->pid)) { + /* Not enough data to have the pid field */ + return; + } + if (pfloghdr_length < (u_int) (offsetof(struct pfloghdr, pid) + sizeof pflhdr->pid)) { + /* Header doesn't include pid field */ + return; + } + pflhdr->pid = SWAPLONG(pflhdr->pid); + + if (caplen < (u_int) (offsetof(struct pfloghdr, rule_uid) + sizeof pflhdr->rule_uid) || + length < (u_int) (offsetof(struct pfloghdr, rule_uid) + sizeof pflhdr->rule_uid)) { + /* Not enough data to have the rule_uid field */ + return; + } + if (pfloghdr_length < (u_int) (offsetof(struct pfloghdr, rule_uid) + sizeof pflhdr->rule_uid)) { + /* Header doesn't include rule_uid field */ + return; + } + pflhdr->rule_uid = SWAPLONG(pflhdr->rule_uid); + + if (caplen < (u_int) (offsetof(struct pfloghdr, rule_pid) + sizeof pflhdr->rule_pid) || + length < (u_int) (offsetof(struct pfloghdr, rule_pid) + sizeof pflhdr->rule_pid)) { + /* Not enough data to have the rule_pid field */ + return; + } + if (pfloghdr_length < (u_int) (offsetof(struct pfloghdr, rule_pid) + sizeof pflhdr->rule_pid)) { + /* Header doesn't include rule_pid field */ + return; + } + pflhdr->rule_pid = SWAPLONG(pflhdr->rule_pid); +} + +/* + * DLT_LINUX_SLL packets with a protocol type of LINUX_SLL_P_CAN or + * LINUX_SLL_P_CANFD have SocketCAN headers in front of the payload, + * with the CAN ID being in host byte order. + * + * When reading a DLT_LINUX_SLL packet, we need to check for those + * packets and convert the CAN ID from the byte order of the host that + * wrote the file to this host's byte order. + */ +static void +swap_linux_sll_header(const struct pcap_pkthdr *hdr, u_char *buf) +{ + u_int caplen = hdr->caplen; + u_int length = hdr->len; + struct sll_header *shdr = (struct sll_header *)buf; + uint16_t protocol; + pcap_can_socketcan_hdr *chdr; + + if (caplen < (u_int) sizeof(struct sll_header) || + length < (u_int) sizeof(struct sll_header)) { + /* Not enough data to have the protocol field */ + return; + } + + protocol = EXTRACT_BE_U_2(&shdr->sll_protocol); + if (protocol != LINUX_SLL_P_CAN && protocol != LINUX_SLL_P_CANFD) + return; + + /* + * SocketCAN packet; fix up the packet's header. + */ + chdr = (pcap_can_socketcan_hdr *)(buf + sizeof(struct sll_header)); + if (caplen < (u_int) sizeof(struct sll_header) + sizeof(chdr->can_id) || + length < (u_int) sizeof(struct sll_header) + sizeof(chdr->can_id)) { + /* Not enough data to have the CAN ID */ + return; + } + chdr->can_id = SWAPLONG(chdr->can_id); +} + +/* + * The same applies for DLT_LINUX_SLL2. + */ +static void +swap_linux_sll2_header(const struct pcap_pkthdr *hdr, u_char *buf) +{ + u_int caplen = hdr->caplen; + u_int length = hdr->len; + struct sll2_header *shdr = (struct sll2_header *)buf; + uint16_t protocol; + pcap_can_socketcan_hdr *chdr; + + if (caplen < (u_int) sizeof(struct sll2_header) || + length < (u_int) sizeof(struct sll2_header)) { + /* Not enough data to have the protocol field */ + return; + } + + protocol = EXTRACT_BE_U_2(&shdr->sll2_protocol); + if (protocol != LINUX_SLL_P_CAN && protocol != LINUX_SLL_P_CANFD) + return; + + /* + * SocketCAN packet; fix up the packet's header. + */ + chdr = (pcap_can_socketcan_hdr *)(buf + sizeof(struct sll2_header)); + if (caplen < (u_int) sizeof(struct sll2_header) + sizeof(chdr->can_id) || + length < (u_int) sizeof(struct sll2_header) + sizeof(chdr->can_id)) { + /* Not enough data to have the CAN ID */ + return; + } + chdr->can_id = SWAPLONG(chdr->can_id); +} + +/* + * The DLT_USB_LINUX and DLT_USB_LINUX_MMAPPED headers are in host + * byte order when capturing (it's supplied directly from a + * memory-mapped buffer shared by the kernel). + * + * When reading a DLT_USB_LINUX or DLT_USB_LINUX_MMAPPED packet, we + * need to convert it from the byte order of the host that wrote the + * file to this host's byte order. + */ +static void +swap_linux_usb_header(const struct pcap_pkthdr *hdr, u_char *buf, + int header_len_64_bytes) +{ + pcap_usb_header_mmapped *uhdr = (pcap_usb_header_mmapped *)buf; + bpf_u_int32 offset = 0; + + /* + * "offset" is the offset *past* the field we're swapping; + * we skip the field *before* checking to make sure + * the captured data length includes the entire field. + */ + + /* + * The URB id is a totally opaque value; do we really need to + * convert it to the reading host's byte order??? + */ + offset += 8; /* skip past id */ + if (hdr->caplen < offset) + return; + uhdr->id = SWAPLL(uhdr->id); + + offset += 4; /* skip past various 1-byte fields */ + + offset += 2; /* skip past bus_id */ + if (hdr->caplen < offset) + return; + uhdr->bus_id = SWAPSHORT(uhdr->bus_id); + + offset += 2; /* skip past various 1-byte fields */ + + offset += 8; /* skip past ts_sec */ + if (hdr->caplen < offset) + return; + uhdr->ts_sec = SWAPLL(uhdr->ts_sec); + + offset += 4; /* skip past ts_usec */ + if (hdr->caplen < offset) + return; + uhdr->ts_usec = SWAPLONG(uhdr->ts_usec); + + offset += 4; /* skip past status */ + if (hdr->caplen < offset) + return; + uhdr->status = SWAPLONG(uhdr->status); + + offset += 4; /* skip past urb_len */ + if (hdr->caplen < offset) + return; + uhdr->urb_len = SWAPLONG(uhdr->urb_len); + + offset += 4; /* skip past data_len */ + if (hdr->caplen < offset) + return; + uhdr->data_len = SWAPLONG(uhdr->data_len); + + if (uhdr->transfer_type == URB_ISOCHRONOUS) { + offset += 4; /* skip past s.iso.error_count */ + if (hdr->caplen < offset) + return; + uhdr->s.iso.error_count = SWAPLONG(uhdr->s.iso.error_count); + + offset += 4; /* skip past s.iso.numdesc */ + if (hdr->caplen < offset) + return; + uhdr->s.iso.numdesc = SWAPLONG(uhdr->s.iso.numdesc); + } else + offset += 8; /* skip USB setup header */ + + /* + * With the old header, there are no isochronous descriptors + * after the header. + * + * With the new header, the actual number of descriptors in + * the header is not s.iso.numdesc, it's ndesc - only the + * first N descriptors, for some value of N, are put into + * the header, and ndesc is set to the actual number copied. + * In addition, if s.iso.numdesc is negative, no descriptors + * are captured, and ndesc is set to 0. + */ + if (header_len_64_bytes) { + /* + * This is either the "version 1" header, with + * 16 bytes of additional fields at the end, or + * a "version 0" header from a memory-mapped + * capture, with 16 bytes of zeroed-out padding + * at the end. Byte swap them as if this were + * a "version 1" header. + */ + offset += 4; /* skip past interval */ + if (hdr->caplen < offset) + return; + uhdr->interval = SWAPLONG(uhdr->interval); + + offset += 4; /* skip past start_frame */ + if (hdr->caplen < offset) + return; + uhdr->start_frame = SWAPLONG(uhdr->start_frame); + + offset += 4; /* skip past xfer_flags */ + if (hdr->caplen < offset) + return; + uhdr->xfer_flags = SWAPLONG(uhdr->xfer_flags); + + offset += 4; /* skip past ndesc */ + if (hdr->caplen < offset) + return; + uhdr->ndesc = SWAPLONG(uhdr->ndesc); + + if (uhdr->transfer_type == URB_ISOCHRONOUS) { + /* swap the values in struct linux_usb_isodesc */ + usb_isodesc *pisodesc; + uint32_t i; + + pisodesc = (usb_isodesc *)(void *)(buf+offset); + for (i = 0; i < uhdr->ndesc; i++) { + offset += 4; /* skip past status */ + if (hdr->caplen < offset) + return; + pisodesc->status = SWAPLONG(pisodesc->status); + + offset += 4; /* skip past offset */ + if (hdr->caplen < offset) + return; + pisodesc->offset = SWAPLONG(pisodesc->offset); + + offset += 4; /* skip past len */ + if (hdr->caplen < offset) + return; + pisodesc->len = SWAPLONG(pisodesc->len); + + offset += 4; /* skip past padding */ + + pisodesc++; + } + } + } +} + +/* + * The DLT_NFLOG "packets" have a mixture of big-endian and host-byte-order + * data. They begin with a fixed-length header with big-endian fields, + * followed by a set of TLVs, where the type and length are in host + * byte order but the values are either big-endian or are a raw byte + * sequence that's the same regardless of the host's byte order. + * + * When reading a DLT_NFLOG packet, we need to convert the type and + * length values from the byte order of the host that wrote the file + * to the byte order of this host. + */ +static void +swap_nflog_header(const struct pcap_pkthdr *hdr, u_char *buf) +{ + u_char *p = buf; + nflog_hdr_t *nfhdr = (nflog_hdr_t *)buf; + nflog_tlv_t *tlv; + u_int caplen = hdr->caplen; + u_int length = hdr->len; + uint16_t size; + + if (caplen < (u_int) sizeof(nflog_hdr_t) || + length < (u_int) sizeof(nflog_hdr_t)) { + /* Not enough data to have any TLVs. */ + return; + } + + if (nfhdr->nflog_version != 0) { + /* Unknown NFLOG version */ + return; + } + + length -= sizeof(nflog_hdr_t); + caplen -= sizeof(nflog_hdr_t); + p += sizeof(nflog_hdr_t); + + while (caplen >= sizeof(nflog_tlv_t)) { + tlv = (nflog_tlv_t *) p; + + /* Swap the type and length. */ + tlv->tlv_type = SWAPSHORT(tlv->tlv_type); + tlv->tlv_length = SWAPSHORT(tlv->tlv_length); + + /* Get the length of the TLV. */ + size = tlv->tlv_length; + if (size % 4 != 0) + size += 4 - size % 4; + + /* Is the TLV's length less than the minimum? */ + if (size < sizeof(nflog_tlv_t)) { + /* Yes. Give up now. */ + return; + } + + /* Do we have enough data for the full TLV? */ + if (caplen < size || length < size) { + /* No. */ + return; + } + + /* Skip over the TLV. */ + length -= size; + caplen -= size; + p += size; + } +} + +static void +swap_pseudo_headers(int linktype, struct pcap_pkthdr *hdr, u_char *data) +{ + /* + * Convert pseudo-headers from the byte order of + * the host on which the file was saved to our + * byte order, as necessary. + */ + switch (linktype) { + + case DLT_PFLOG: + swap_pflog_header(hdr, data); + break; + + case DLT_LINUX_SLL: + swap_linux_sll_header(hdr, data); + break; + + case DLT_LINUX_SLL2: + swap_linux_sll2_header(hdr, data); + break; + + case DLT_USB_LINUX: + swap_linux_usb_header(hdr, data, 0); + break; + + case DLT_USB_LINUX_MMAPPED: + swap_linux_usb_header(hdr, data, 1); + break; + + case DLT_NFLOG: + swap_nflog_header(hdr, data); + break; + } +} + +void +pcap_post_process(int linktype, int swapped, struct pcap_pkthdr *hdr, + u_char *data) +{ + if (swapped) + swap_pseudo_headers(linktype, hdr, data); + + fixup_pcap_pkthdr(linktype, hdr, data); +} + +void +fixup_pcap_pkthdr(int linktype, struct pcap_pkthdr *hdr, const u_char *data) +{ + const pcap_usb_header_mmapped *usb_hdr; + + usb_hdr = (const pcap_usb_header_mmapped *) data; + if (linktype == DLT_USB_LINUX_MMAPPED && + hdr->caplen >= sizeof (pcap_usb_header_mmapped)) { + /* + * In older versions of libpcap, in memory-mapped captures, + * the "on-the-bus length" for completion events for + * incoming isochronous transfers was miscalculated; it + * needed to be calculated based on the* offsets and lengths + * in the descriptors, not on the raw URB length, but it + * wasn't. + * + * If this packet contains transferred data (yes, data_flag + * is 0 if we *do* have data), and the total on-the-network + * length is equal to the value calculated from the raw URB + * length, then it might be one of those transfers. + * + * We only do this if we have the full USB pseudo-header. + */ + if (!usb_hdr->data_flag && + hdr->len == sizeof(pcap_usb_header_mmapped) + + (usb_hdr->ndesc * sizeof (usb_isodesc)) + usb_hdr->urb_len) { + /* + * It might need fixing; fix it if it's a completion + * event for an incoming isochronous transfer. + */ + fix_linux_usb_mmapped_length(hdr, data); + } + } +} diff --git a/pcap-util.h b/pcap-util.h new file mode 100644 index 000000000000..de958191cbef --- /dev/null +++ b/pcap-util.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 1993, 1994, 1995, 1996, 1997 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * pcap-util.h - common code for various files + */ + +/* + * We use the "receiver-makes-right" approach to byte order; + * because time is at a premium when we are writing the file. + * In other words, the pcap_file_header and pcap_pkthdr, + * records are written in host byte order. + * Note that the bytes of packet data are written out in the order in + * which they were received, so multi-byte fields in packets are not + * written in host byte order, they're written in whatever order the + * sending machine put them in. + * + * We also use this for fixing up packet data headers from a remote + * capture, where the server may have a different byte order from the + * client. + * + * ntoh[ls] aren't sufficient because we might need to swap on a big-endian + * machine (if the file was written in little-end order). + */ +#define SWAPLONG(y) \ + (((((u_int)(y))&0xff)<<24) | \ + ((((u_int)(y))&0xff00)<<8) | \ + ((((u_int)(y))&0xff0000)>>8) | \ + ((((u_int)(y))>>24)&0xff)) +#define SWAPSHORT(y) \ + ((u_short)(((((u_int)(y))&0xff)<<8) | \ + ((((u_int)(y))&0xff00)>>8))) + +extern void pcap_post_process(int linktype, int swapped, + struct pcap_pkthdr *hdr, u_char *data); + +extern void fixup_pcap_pkthdr(int linktype, struct pcap_pkthdr *hdr, + const u_char *data); + diff --git a/pcap.3pcap.in b/pcap.3pcap.in index 80101403b2b6..e98932d857c8 100644 --- a/pcap.3pcap.in +++ b/pcap.3pcap.in @@ -17,7 +17,7 @@ .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" -.TH PCAP 3PCAP "25 July 2018" +.TH PCAP 3PCAP "9 September 2020" .SH NAME pcap \- Packet Capture library .SH SYNOPSIS @@ -35,12 +35,69 @@ on the network, even those destined for other hosts, are accessible through this mechanism. It also supports saving captured packets to a ``savefile'', and reading packets from a ``savefile''. +.SS Initializing +.BR pcap_init () +initializes the library. It takes an argument giving options; +currently, the options are: +.TP +.B PCAP_CHAR_ENC_LOCAL +Treat all strings supplied as arguments, and return all strings to the +caller, as being in the local character encoding. +.TP +.B PCAP_CHAR_ENC_UTF_8 +Treat all strings supplied as arguments, and return all strings to the +caller, as being in UTF-8. +.PP +On UNIX-like systems, the local character encoding is assumed to be +UTF-8, so no character encoding transformations are done. +.PP +On Windows, the local character encoding is the local ANSI code page. +.PP +If +.BR pcap_init () +is called, the deprecated +.BR pcap_lookupdev () +routine always fails, so it should not be used, and, on Windows, +.BR pcap_create () +does not attempt to handle UTF-16LE strings. +.PP +If +.BR pcap_init () +is not called, strings are treated as being in the local ANSI code page +on Windows, +.BR pcap_lookupdev () +will succeed if there is a device on which to capture, and +.BR pcap_create () +makes an attempt to check whether the string passed as an argument is a +UTF-16LE string - note that this attempt is unsafe, as it may run past +the end of the string - to handle +.BR pcap_lookupdev () +returning a UTF-16LE string. Programs that don't call +.BR pcap_init () +should, on Windows, call +.BR pcap_wsockinit () +to initialize Winsock; this is not necessary if +.BR pcap_init () +is called, as +.BR pcap_init () +will initialize Winsock itself on Windows. +.TP +.B Routines +.RS +.TP +.BR pcap_init (3PCAP) +initialize the library +.RE .SS Opening a capture handle for reading To open a handle for a live capture, given the name of the network or other interface on which the capture should be done, call .BR pcap_create (), set the appropriate options on the handle, and then activate it with .BR pcap_activate (). +If +.BR pcap_activate () +fails, the handle should be closed with +.BR pcap_close (). .PP To obtain a list of devices that can be opened for a live capture, call .BR pcap_findalldevs (); @@ -121,7 +178,9 @@ Note that even if an application does not set promiscuous mode, the adapter could well be in promiscuous mode for some other reason. .IP For now, this doesn't work on the "any" device; if an argument of "any" -or NULL is supplied, the setting of promiscuous mode is ignored. +or +.B NULL +is supplied, the setting of promiscuous mode is ignored. .IP Promiscuous mode is set with .BR pcap_set_promisc (). @@ -213,7 +272,7 @@ On some platforms, the time stamp given to packets on live captures can come from different sources that can have different resolutions or that can have different relationships to the time values for the current time supplied by routines on the native operating system. See -.BR pcap-tstamp (@MAN_MISC_INFO@) +.BR \%pcap-tstamp (@MAN_MISC_INFO@) for a list of time stamp types. .IP The time stamp type is set with @@ -249,7 +308,7 @@ that device. A user can be given that privilege by, for example, adding that privilege to the user's .B defaultpriv key with the -.B usermod (@MAN_ADMIN_COMMANDS@) +.BR usermod (@MAN_ADMIN_COMMANDS@) command. .TP .B Under HP-UX with DLPI: @@ -262,14 +321,11 @@ setuid to root. .TP .B Under Linux: You must be root or the application capturing packets must be installed -setuid to root (unless your distribution has a kernel +setuid to root, unless your distribution has a kernel that supports capability bits such as CAP_NET_RAW and code to allow those capability bits to be given to particular accounts and to cause those bits to be set on a user's initial processes when they log in, in -which case you must have CAP_NET_RAW in order to capture and -CAP_NET_ADMIN to enumerate network devices with, for example, the -.B \-D -flag). +which case you must have CAP_NET_RAW in order to capture. .TP .B Under ULTRIX and Digital UNIX/Tru64 UNIX: Any user may capture network traffic. @@ -320,6 +376,8 @@ any given link-layer header type, such as for Ethernet. For example, the "any" device on Linux will have a link-layer header type of .B DLT_LINUX_SLL +or +.B DLT_LINUX_SLL2 even if all devices on the system at the time the "any" device is opened have some other data link type, such as .B DLT_EN10MB @@ -515,7 +573,11 @@ set link-layer header type for a device get name for a link-layer header type .TP .BR pcap_datalink_val_to_description (3PCAP) +.PD 0 +.TP +.BR pcap_datalink_val_to_description_or_dlt (3PCAP) get description for a link-layer header type +.PD .TP .BR pcap_datalink_name_to_val (3PCAP) get link-layer header type corresponding to a name @@ -585,7 +647,9 @@ packet. It returns a .I const u_char to the first .B caplen -bytes of the packet on success, and NULL on error. +bytes of the packet on success, and +.B NULL +on error. .PP .BR pcap_next_ex () is passed two pointer arguments, one of which points to a @@ -648,7 +712,7 @@ from the device. Not all handles have such a descriptor available; .BR pcap_get_selectable_fd () will return -.B PCAP_ERROR +.B \-1 if no such descriptor is available. If no such descriptor is available, this may be because the device must be polled periodically for packets; in that case, @@ -656,10 +720,12 @@ periodically for packets; in that case, will return a pointer to a .B struct timeval whose value can be used as a timeout in those routines. When the -routine returns, an attmept should be made to read packets from the +routine returns, an attempt should be made to read packets from the device. If .BR pcap_get_required_select_timeout () -returns NULL, no such timeout is available, and those routines cannot be +returns +.BR NULL , +no such timeout is available, and those routines cannot be used with the device. .PP In addition, for various @@ -718,18 +784,17 @@ get the state of non-blocking mode for a attempt to get a descriptor for a .B pcap_t that can be used in calls such as -.BR select (2) +.BR select () and -.BR poll (2) +.BR poll () .TP .BR pcap_get_required_select_timeout (3PCAP) -if no descriptor usable with -.BR select (2) +attempt to get a timeout required for using a +.B pcap_t +in calls such as +.BR select () and -.BR poll (2) -is available for the -.BR pcap_t , -attempt to get a timeout usable with those routines +.BR poll () .RE .SS Filters In order to cause only certain packets to be returned when reading @@ -739,7 +804,7 @@ copying ``uninteresting'' packets from the kernel to user mode. .PP A filter can be specified as a text string; the syntax and semantics of the string are as described by -.BR pcap-filter (@MAN_MISC_INFO@). +.BR \%pcap-filter (@MAN_MISC_INFO@). A filter string is compiled into a program in a pseudo-machine-language by .BR pcap_compile () @@ -829,13 +894,19 @@ call .BR pcap_dump_open (3PCAP) open a .B pcap_dumper_t -for a ``savefile``, given a pathname +for a ``savefile``, given a pathname, replacing any existing data +.TP +.BR pcap_dump_open_append (3PCAP) +open a +.B pcap_dumper_t +for a ``savefile``, given a pathname, appending to the existing data .TP .BR pcap_dump_fopen (3PCAP) open a .B pcap_dumper_t for a ``savefile``, given a -.B "FILE\ *" +.BR "FILE\ *" , +assuming an empty file .TP .BR pcap_dump_close (3PCAP) close a @@ -893,8 +964,9 @@ for a live capture, using .BR pcap_inject () or .BR pcap_sendpacket (). -(The two routines exist for compatibility with both OpenBSD and WinPcap; -they perform the same function, but have different return values.) +(The two routines exist for compatibility with both OpenBSD and +WinPcap/Npcap; they perform the same function, but have different return +values.) .TP .B Routines .RS @@ -958,8 +1030,12 @@ use an script or some other configuration script to check whether the libpcap 1.0 APIs are available and use them only if they are. .SH SEE ALSO -autoconf(1), tcpdump(1), tcpslice(1), pcap-filter(@MAN_MISC_INFO@), pfconfig(8), -usermod(@MAN_ADMIN_COMMANDS@) +.BR autoconf (1), +.BR tcpdump (1), +.BR tcpslice (1), +.BR \%pcap-filter (@MAN_MISC_INFO@), +.BR pfconfig (8), +.BR usermod (@MAN_ADMIN_COMMANDS@) .SH AUTHORS The original authors of libpcap are: .LP @@ -974,9 +1050,9 @@ The current version is available from "The Tcpdump Group"'s Web site at .I https://www.tcpdump.org/ .RE .SH BUGS -To report a security issue please send an e-mail to security@tcpdump.org. +To report a security issue please send an e-mail to \%security@tcpdump.org. .LP To report bugs and other problems, contribute patches, request a feature, provide generic feedback etc please see the file -.I CONTRIBUTING +.I CONTRIBUTING.md in the libpcap source tree root. diff --git a/pcap.c b/pcap.c index 5c8b51a25074..ef1bbb71f87f 100644 --- a/pcap.c +++ b/pcap.c @@ -53,7 +53,6 @@ struct rtentry; /* declarations in */ #include #endif /* _WIN32 */ -#include #include #include #include @@ -62,11 +61,9 @@ struct rtentry; /* declarations in */ #endif #include #include -#ifdef HAVE_LIMITS_H #include -#else -#define INT_MAX 2147483647 -#endif + +#include "diag-control.h" #ifdef HAVE_OS_PROTO_H #include "os-proto.h" @@ -96,7 +93,7 @@ struct rtentry; /* declarations in */ #include "pcap-tc.h" #endif /* HAVE_TC_API */ -#ifdef PCAP_SUPPORT_USB +#ifdef PCAP_SUPPORT_LINUX_USBMON #include "pcap-usb-linux.h" #endif @@ -124,55 +121,191 @@ struct rtentry; /* declarations in */ #include "pcap-rdmasniff.h" #endif +#ifdef PCAP_SUPPORT_DPDK +#include "pcap-dpdk.h" +#endif + +#ifdef HAVE_AIRPCAP_API +#include "pcap-airpcap.h" +#endif + #ifdef _WIN32 /* - * DllMain(), required when built as a Windows DLL. + * To quote the WSAStartup() documentation: + * + * The WSAStartup function typically leads to protocol-specific helper + * DLLs being loaded. As a result, the WSAStartup function should not + * be called from the DllMain function in a application DLL. This can + * potentially cause deadlocks. + * + * and the WSACleanup() documentation: + * + * The WSACleanup function typically leads to protocol-specific helper + * DLLs being unloaded. As a result, the WSACleanup function should not + * be called from the DllMain function in a application DLL. This can + * potentially cause deadlocks. + * + * So we don't initialize Winsock in a DllMain() routine. + * + * pcap_init() should be called to initialize pcap on both UN*X and + * Windows; it will initialize Winsock on Windows. (It will also be + * initialized as needed if pcap_init() hasn't been called.) */ -BOOL WINAPI DllMain( - HANDLE hinstDLL, - DWORD dwReason, - LPVOID lpvReserved -) + +/* + * Start Winsock. + * Internal routine. + */ +static int +internal_wsockinit(char *errbuf) { - return (TRUE); + WORD wVersionRequested; + WSADATA wsaData; + static int err = -1; + static int done = 0; + int status; + + if (done) + return (err); + + /* + * Versions of Windows that don't support Winsock 2.2 are + * too old for us. + */ + wVersionRequested = MAKEWORD(2, 2); + status = WSAStartup(wVersionRequested, &wsaData); + done = 1; + if (status != 0) { + if (errbuf != NULL) { + pcap_fmt_errmsg_for_win32_err(errbuf, PCAP_ERRBUF_SIZE, + status, "WSAStartup() failed"); + } + return (err); + } + atexit ((void(*)(void))WSACleanup); + err = 0; + return (err); } /* - * Start WinSock. * Exported in case some applications using WinPcap/Npcap called it, * even though it wasn't exported. */ int wsockinit(void) { - WORD wVersionRequested; - WSADATA wsaData; - static int err = -1; - static int done = 0; - - if (done) - return (err); - - wVersionRequested = MAKEWORD( 1, 1); - err = WSAStartup( wVersionRequested, &wsaData ); - atexit ((void(*)(void))WSACleanup); - done = 1; - - if ( err != 0 ) - err = -1; - return (err); + return (internal_wsockinit(NULL)); } /* * This is the exported function; new programs should call this. + * *Newer* programs should call pcap_init(). */ int pcap_wsockinit(void) { - return (wsockinit()); + return (internal_wsockinit(NULL)); } #endif /* _WIN32 */ +/* + * Do whatever initialization is needed for libpcap. + * + * The argument specifies whether we use the local code page or UTF-8 + * for strings; on UN*X, we just assume UTF-8 in places where the encoding + * would matter, whereas, on Windows, we use the local code page for + * PCAP_CHAR_ENC_LOCAL and UTF-8 for PCAP_CHAR_ENC_UTF_8. + * + * On Windows, we also disable the hack in pcap_create() to deal with + * being handed UTF-16 strings, because if the user calls this they're + * explicitly declaring that they will either be passing local code + * page strings or UTF-8 strings, so we don't need to allow UTF-16LE + * strings to be passed. For good measure, on Windows *and* UN*X, + * we disable pcap_lookupdev(), to prevent anybody from even + * *trying* to pass the result of pcap_lookupdev() - which might be + * UTF-16LE on Windows, for ugly compatibility reasons - to pcap_create() + * or pcap_open_live() or pcap_open(). + * + * Returns 0 on success, -1 on error. + */ +int pcap_new_api; /* pcap_lookupdev() always fails */ +int pcap_utf_8_mode; /* Strings should be in UTF-8. */ + +int +pcap_init(unsigned int opts, char *errbuf) +{ + static int initialized; + + /* + * Don't allow multiple calls that set different modes; that + * may mean a library is initializing pcap in one mode and + * a program using that library, or another library used by + * that program, is initializing it in another mode. + */ + switch (opts) { + + case PCAP_CHAR_ENC_LOCAL: + /* Leave "UTF-8 mode" off. */ + if (initialized) { + if (pcap_utf_8_mode) { + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "Multiple pcap_init calls with different character encodings"); + return (PCAP_ERROR); + } + } + break; + + case PCAP_CHAR_ENC_UTF_8: + /* Turn on "UTF-8 mode". */ + if (initialized) { + if (!pcap_utf_8_mode) { + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "Multiple pcap_init calls with different character encodings"); + return (PCAP_ERROR); + } + } + pcap_utf_8_mode = 1; + break; + + default: + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Unknown options specified"); + return (PCAP_ERROR); + } + + /* + * Turn the appropriate mode on for error messages; those routines + * are also used in rpcapd, which has no access to pcap's internal + * UTF-8 mode flag, so we have to call a routine to set its + * UTF-8 mode flag. + */ + pcap_fmt_set_encoding(opts); + + if (initialized) { + /* + * Nothing more to do; for example, on Windows, we've + * already initialized Winsock. + */ + return (0); + } + +#ifdef _WIN32 + /* + * Now set up Winsock. + */ + if (internal_wsockinit(errbuf) == -1) { + /* Failed. */ + return (PCAP_ERROR); + } +#endif + + /* + * We're done. + */ + initialized = 1; + pcap_new_api = 1; + return (0); +} + /* * String containing the library version. * Not explicitly exported via a header file - the right API to use @@ -191,12 +324,12 @@ pcap_set_not_initialized_message(pcap_t *pcap) { if (pcap->activated) { /* A module probably forgot to set the function pointer */ - (void)pcap_snprintf(pcap->errbuf, sizeof(pcap->errbuf), + (void)snprintf(pcap->errbuf, sizeof(pcap->errbuf), "This operation isn't properly handled by that device"); return; } /* in case the caller doesn't check for PCAP_ERROR_NOT_ACTIVATED */ - (void)pcap_snprintf(pcap->errbuf, sizeof(pcap->errbuf), + (void)snprintf(pcap->errbuf, sizeof(pcap->errbuf), "This handle hasn't been activated yet"); } @@ -210,7 +343,7 @@ pcap_read_not_initialized(pcap_t *pcap, int cnt _U_, pcap_handler callback _U_, } static int -pcap_inject_not_initialized(pcap_t *pcap, const void * buf _U_, size_t size _U_) +pcap_inject_not_initialized(pcap_t *pcap, const void * buf _U_, int size _U_) { pcap_set_not_initialized_message(pcap); /* this means 'not initialized' */ @@ -258,7 +391,7 @@ pcap_stats_not_initialized(pcap_t *pcap, struct pcap_stat *ps _U_) } #ifdef _WIN32 -struct pcap_stat * +static struct pcap_stat * pcap_stats_ex_not_initialized(pcap_t *pcap, int *pcap_stat_size _U_) { pcap_set_not_initialized_message(pcap); @@ -313,7 +446,8 @@ pcap_oid_set_request_not_initialized(pcap_t *pcap, bpf_u_int32 oid _U_, } static u_int -pcap_sendqueue_transmit_not_initialized(pcap_t *pcap, pcap_send_queue* queue, int sync) +pcap_sendqueue_transmit_not_initialized(pcap_t *pcap, pcap_send_queue* queue _U_, + int sync _U_) { pcap_set_not_initialized_message(pcap); return (0); @@ -479,7 +613,9 @@ pcap_next_ex(pcap_t *p, struct pcap_pkthdr **pkt_header, * Return codes for pcap_offline_read() are: * - 0: EOF * - -1: error - * - >1: OK + * - >0: OK - result is number of packets read, so + * it will be 1 in this case, as we've passed + * a maximum packet count of 1 * The first one ('0') conflicts with the return code of * 0 from pcap_read() meaning "no packets arrived before * the timeout expired", so we map it to -2 so you can @@ -498,7 +634,9 @@ pcap_next_ex(pcap_t *p, struct pcap_pkthdr **pkt_header, * - 0: timeout * - -1: error * - -2: loop was broken out of with pcap_breakloop() - * - >1: OK + * - >0: OK, result is number of packets captured, so + * it will be 1 in this case, as we've passed + * a maximum packet count of 1 * The first one ('0') conflicts with the return code of 0 from * pcap_offline_read() meaning "end of file". */ @@ -534,7 +672,7 @@ static struct capture_source_type { #ifdef PCAP_SUPPORT_BT_MONITOR { bt_monitor_findalldevs, bt_monitor_create }, #endif -#ifdef PCAP_SUPPORT_USB +#ifdef PCAP_SUPPORT_LINUX_USBMON { usb_findalldevs, usb_create }, #endif #ifdef PCAP_SUPPORT_NETFILTER @@ -548,6 +686,12 @@ static struct capture_source_type { #endif #ifdef PCAP_SUPPORT_RDMASNIFF { rdmasniff_findalldevs, rdmasniff_create }, +#endif +#ifdef PCAP_SUPPORT_DPDK + { pcap_dpdk_findalldevs, pcap_dpdk_create }, +#endif +#ifdef HAVE_AIRPCAP_API + { airpcap_findalldevs, airpcap_create }, #endif { NULL, NULL } }; @@ -628,39 +772,26 @@ dup_sockaddr(struct sockaddr *sa, size_t sa_length) * * The figure of merit, which is lower the "better" the interface is, * has the uppermost bit set if the interface isn't running, the bit - * below that set if the interface isn't up, the bit below that set - * if the interface is a loopback interface, and the interface index - * in the 29 bits below that. (Yes, we assume u_int is 32 bits.) + * below that set if the interface isn't up, the bit below that + * set if the interface is a loopback interface, and the bit below + * that set if it's the "any" interface. + * + * Note: we don't sort by unit number because 1) not all interfaces have + * a unit number (systemd, for example, might assign interface names + * based on the interface's MAC address or on the physical location of + * the adapter's connector), and 2) if the name does end with a simple + * unit number, it's not a global property of the interface, it's only + * useful as a sort key for device names with the same prefix, so xyz0 + * shouldn't necessarily sort before abc2. This means that interfaces + * with the same figure of merit will be sorted by the order in which + * the mechanism from which we're getting the interfaces supplies them. */ static u_int get_figure_of_merit(pcap_if_t *dev) { - const char *cp; u_int n; - if (strcmp(dev->name, "any") == 0) { - /* - * Give the "any" device an artificially high instance - * number, so it shows up after all other non-loopback - * interfaces. - */ - n = 0x1FFFFFFF; /* 29 all-1 bits */ - } else { - /* - * A number at the end of the device name string is - * assumed to be an instance number. Add 1 to the - * instance number, and use 0 for "no instance - * number", so we don't put "no instance number" - * devices and "instance 0" devices together. - */ - cp = dev->name + strlen(dev->name) - 1; - while (cp-1 >= dev->name && *(cp-1) >= '0' && *(cp-1) <= '9') - cp--; - if (*cp >= '0' && *cp <= '9') - n = atoi(cp) + 1; - else - n = 0; - } + n = 0; if (!(dev->flags & PCAP_IF_RUNNING)) n |= 0x80000000; if (!(dev->flags & PCAP_IF_UP)) @@ -687,6 +818,13 @@ get_figure_of_merit(pcap_if_t *dev) if (dev->flags & PCAP_IF_LOOPBACK) n |= 0x10000000; + /* + * Sort the "any" device before loopback and disconnected devices, + * but after all other devices. + */ + if (strcmp(dev->name, "any") == 0) + n |= 0x08000000; + return (n); } @@ -887,7 +1025,7 @@ find_or_add_if(pcap_if_list_t *devlistp, const char *name, * see if it looks like a loopback device. */ if (name[0] == 'l' && name[1] == 'o' && - (isdigit((unsigned char)(name[2])) || name[2] == '\0') + (PCAP_ISDIGIT(name[2]) || name[2] == '\0')) pcap_flags |= PCAP_IF_LOOPBACK; #endif #ifdef IFF_UP @@ -1368,6 +1506,22 @@ pcap_lookupdev(char *errbuf) static char device[IF_NAMESIZE + 1]; char *ret; + /* + * We disable this in "new API" mode, because 1) in WinPcap/Npcap, + * it may return UTF-16 strings, for backwards-compatibility + * reasons, and we're also disabling the hack to make that work, + * for not-going-past-the-end-of-a-string reasons, and 2) we + * want its behavior to be consistent. + * + * In addition, it's not thread-safe, so we've marked it as + * deprecated. + */ + if (pcap_new_api) { + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "pcap_lookupdev() is deprecated and is not supported in programs calling pcap_init()"); + return (NULL); + } + if (pcap_findalldevs(&alldevs, errbuf) == -1) return (NULL); @@ -1431,7 +1585,7 @@ pcap_lookupnet(const char *device, bpf_u_int32 *netp, bpf_u_int32 *maskp, #ifdef PCAP_SUPPORT_BT || strstr(device, "bluetooth") != NULL #endif -#ifdef PCAP_SUPPORT_USB +#ifdef PCAP_SUPPORT_LINUX_USBMON || strstr(device, "usbmon") != NULL #endif #ifdef HAVE_SNF_API @@ -1440,6 +1594,9 @@ pcap_lookupnet(const char *device, bpf_u_int32 *netp, bpf_u_int32 *maskp, #ifdef PCAP_SUPPORT_NETMAP || strncmp(device, "netmap:", 7) == 0 || strncmp(device, "vale", 4) == 0 +#endif +#ifdef PCAP_SUPPORT_DPDK + || strncmp(device, "dpdk:", 5) == 0 #endif ) { *netp = *maskp = 0; @@ -1460,7 +1617,7 @@ pcap_lookupnet(const char *device, bpf_u_int32 *netp, bpf_u_int32 *maskp, (void)pcap_strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCGIFADDR, (char *)&ifr) < 0) { if (errno == EADDRNOTAVAIL) { - (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: no IPv4 address assigned", device); } else { pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, @@ -1493,7 +1650,7 @@ pcap_lookupnet(const char *device, bpf_u_int32 *netp, bpf_u_int32 *maskp, else if (IN_CLASSC(*netp)) *maskp = IN_CLASSC_NET; else { - (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, "inet class for 0x%x unknown", *netp); return (-1); } @@ -1685,7 +1842,8 @@ pcap_parse_source(const char *source, char **schemep, char **userinfop, * * XXX - %-escaping? */ - if (pcap_strcasecmp(scheme, "rpcap") == 0 && + if ((pcap_strcasecmp(scheme, "rpcap") == 0 || + pcap_strcasecmp(scheme, "rpcaps") == 0) && strchr(colonp + 3, '/') == NULL) { /* * Local device. @@ -1788,7 +1946,7 @@ pcap_parse_source(const char *source, char **schemep, char **userinfop, /* * There's no closing square bracket. */ - pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, + snprintf(ebuf, PCAP_ERRBUF_SIZE, "IP-literal in URL doesn't end with ]"); free(userinfo); free(authority); @@ -1801,7 +1959,7 @@ pcap_parse_source(const char *source, char **schemep, char **userinfop, * There's extra crud after the * closing square bracketn. */ - pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, + snprintf(ebuf, PCAP_ERRBUF_SIZE, "Extra text after IP-literal in URL"); free(userinfo); free(authority); @@ -1896,8 +2054,8 @@ pcap_parse_source(const char *source, char **schemep, char **userinfop, } int -pcap_createsrcstr(char *source, int type, const char *host, const char *port, - const char *name, char *errbuf) +pcap_createsrcstr_ex(char *source, int type, const char *host, const char *port, + const char *name, unsigned char uses_ssl, char *errbuf) { switch (type) { @@ -1907,13 +2065,15 @@ pcap_createsrcstr(char *source, int type, const char *host, const char *port, pcap_strlcat(source, name, PCAP_BUF_SIZE); return (0); } else { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "The file name cannot be NULL."); return (-1); } case PCAP_SRC_IFREMOTE: - pcap_strlcpy(source, PCAP_SRC_IF_STRING, PCAP_BUF_SIZE); + pcap_strlcpy(source, + (uses_ssl ? "rpcaps://" : PCAP_SRC_IF_STRING), + PCAP_BUF_SIZE); if (host != NULL && *host != '\0') { if (strchr(host, ':') != NULL) { /* @@ -1934,7 +2094,7 @@ pcap_createsrcstr(char *source, int type, const char *host, const char *port, pcap_strlcat(source, "/", PCAP_BUF_SIZE); } else { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "The host name cannot be NULL."); return (-1); } @@ -1953,15 +2113,23 @@ pcap_createsrcstr(char *source, int type, const char *host, const char *port, return (0); default: - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "The interface type is not valid."); return (-1); } } + int -pcap_parsesrcstr(const char *source, int *type, char *host, char *port, - char *name, char *errbuf) +pcap_createsrcstr(char *source, int type, const char *host, const char *port, + const char *name, char *errbuf) +{ + return (pcap_createsrcstr_ex(source, type, host, port, name, 0, errbuf)); +} + +int +pcap_parsesrcstr_ex(const char *source, int *type, char *host, char *port, + char *name, unsigned char *uses_ssl, char *errbuf) { char *scheme, *tmpuserinfo, *tmphost, *tmpport, *tmppath; @@ -1972,6 +2140,8 @@ pcap_parsesrcstr(const char *source, int *type, char *host, char *port, *port = '\0'; if (name) *name = '\0'; + if (uses_ssl) + *uses_ssl = 0; /* Parse the source string */ if (pcap_parse_source(source, &scheme, &tmpuserinfo, &tmphost, @@ -1997,16 +2167,24 @@ pcap_parsesrcstr(const char *source, int *type, char *host, char *port, return (0); } - if (strcmp(scheme, "rpcap") == 0) { + int is_rpcap = 0; + if (strcmp(scheme, "rpcaps") == 0) { + is_rpcap = 1; + if (uses_ssl) *uses_ssl = 1; + } else if (strcmp(scheme, "rpcap") == 0) { + is_rpcap = 1; + } + + if (is_rpcap) { /* - * rpcap:// + * rpcap[s]:// * * pcap_parse_source() has already handled the case of - * rpcap://device + * rpcap[s]://device */ if (host && tmphost) { if (tmpuserinfo) - pcap_snprintf(host, PCAP_BUF_SIZE, "%s@%s", + snprintf(host, PCAP_BUF_SIZE, "%s@%s", tmpuserinfo, tmphost); else pcap_strlcpy(host, tmphost, PCAP_BUF_SIZE); @@ -2056,6 +2234,13 @@ pcap_parsesrcstr(const char *source, int *type, char *host, char *port, free(scheme); return (0); } + +int +pcap_parsesrcstr(const char *source, int *type, char *host, char *port, + char *name, char *errbuf) +{ + return (pcap_parsesrcstr_ex(source, type, host, port, name, NULL, errbuf)); +} #endif pcap_t * @@ -2089,19 +2274,31 @@ pcap_create(const char *device, char *errbuf) * string, not a string in the local code page. * * To work around that, we check whether the string - * looks as if it might be a UTF-16LE strinh and, if + * looks as if it might be a UTF-16LE string and, if * so, convert it back to the local code page's * extended ASCII. * - * XXX - you *cannot* reliably detect whether a - * string is UTF-16LE or not; "a" could either - * be a one-character ASCII string or the first - * character of a UTF-16LE string. This particular - * version of this heuristic dates back to WinPcap - * 4.1.1; PacketOpenAdapter() does uses the same - * heuristic, with the exact same vulnerability. + * We disable that check in "new API" mode, because: + * + * 1) You *cannot* reliably detect whether a + * string is UTF-16LE or not; "a" could either + * be a one-character ASCII string or the first + * character of a UTF-16LE string. + * + * 2) Doing that test can run past the end of + * the string, if it's a 1-character ASCII + * string + * + * This particular version of this heuristic dates + * back to WinPcap 4.1.1; PacketOpenAdapter() does + * uses the same heuristic, with the exact same + * vulnerability. + * + * That's why we disable this in "new API" mode. + * We keep it around in legacy mode for backwards + * compatibility. */ - if (device[0] != '\0' && device[1] == '\0') { + if (!pcap_new_api && device[0] != '\0' && device[1] == '\0') { size_t length; length = wcslen((wchar_t *)device); @@ -2113,7 +2310,7 @@ pcap_create(const char *device, char *errbuf) return (NULL); } - pcap_snprintf(device_str, length + 1, "%ws", + snprintf(device_str, length + 1, "%ws", (const wchar_t *)device); } else #endif @@ -2225,36 +2422,31 @@ initialize_ops(pcap_t *p) * be used for pcap_next()/pcap_next_ex(). */ p->oneshot_callback = pcap_oneshot; + + /* + * Default breakloop operation - implementations can override + * this, but should call pcap_breakloop_common() before doing + * their own logic. + */ + p->breakloop_op = pcap_breakloop_common; } static pcap_t * -pcap_alloc_pcap_t(char *ebuf, size_t size) +pcap_alloc_pcap_t(char *ebuf, size_t total_size, size_t private_offset) { char *chunk; pcap_t *p; /* - * Allocate a chunk of memory big enough for a pcap_t - * plus a structure following it of size "size". The - * structure following it is a private data structure - * for the routines that handle this pcap_t. - * - * The structure following it must be aligned on - * the appropriate alignment boundary for this platform. - * We align on an 8-byte boundary as that's probably what - * at least some platforms do, even with 32-bit integers, - * and because we can't be sure that some values won't - * require 8-byte alignment even on platforms with 32-bit - * integers. + * total_size is the size of a structure containing a pcap_t + * followed by a private structure. */ -#define PCAP_T_ALIGNED_SIZE ((sizeof(pcap_t) + 7U) & ~0x7U) - chunk = malloc(PCAP_T_ALIGNED_SIZE + size); + chunk = calloc(total_size, 1); if (chunk == NULL) { pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, errno, "malloc"); return (NULL); } - memset(chunk, 0, PCAP_T_ALIGNED_SIZE + size); /* * Get a pointer to the pcap_t at the beginning. @@ -2271,26 +2463,24 @@ pcap_alloc_pcap_t(char *ebuf, size_t size) #endif /* MSDOS */ #endif /* _WIN32 */ - if (size == 0) { - /* No private data was requested. */ - p->priv = NULL; - } else { - /* - * Set the pointer to the private data; that's the structure - * of size "size" following the pcap_t. - */ - p->priv = (void *)(chunk + PCAP_T_ALIGNED_SIZE); - } + /* + * private_offset is the offset, in bytes, of the private + * data from the beginning of the structure. + * + * Set the pointer to the private data; that's private_offset + * bytes past the pcap_t. + */ + p->priv = (void *)(chunk + private_offset); return (p); } pcap_t * -pcap_create_common(char *ebuf, size_t size) +pcap_create_common(char *ebuf, size_t total_size, size_t private_offset) { pcap_t *p; - p = pcap_alloc_pcap_t(ebuf, size); + p = pcap_alloc_pcap_t(ebuf, total_size, private_offset); if (p == NULL) return (NULL); @@ -2342,7 +2532,7 @@ int pcap_check_activated(pcap_t *p) { if (p->activated) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "can't perform " + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "can't perform " " operation on activated capture"); return (-1); } @@ -2550,7 +2740,7 @@ pcap_activate(pcap_t *p) * handle errors other than PCAP_ERROR, return the * error message corresponding to the status. */ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s", pcap_statustostr(status)); } @@ -2608,7 +2798,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, char *er NULL, errbuf)); } if (srctype == PCAP_SRC_FILE) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "unknown URL scheme \"file\""); + snprintf(errbuf, PCAP_ERRBUF_SIZE, "unknown URL scheme \"file\""); return (NULL); } if (srctype == PCAP_SRC_IFLOCAL) { @@ -2654,27 +2844,51 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, char *er goto fail; return (p); fail: - if (status == PCAP_ERROR) - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %.*s", device, - PCAP_ERRBUF_SIZE - 3, p->errbuf); - else if (status == PCAP_ERROR_NO_SUCH_DEVICE || + if (status == PCAP_ERROR) { + /* + * Another buffer is a bit cumbersome, but it avoids + * -Wformat-truncation. + */ + char trimbuf[PCAP_ERRBUF_SIZE - 5]; /* 2 bytes shorter */ + + pcap_strlcpy(trimbuf, p->errbuf, sizeof(trimbuf)); + snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %.*s", device, + PCAP_ERRBUF_SIZE - 3, trimbuf); + } else if (status == PCAP_ERROR_NO_SUCH_DEVICE || status == PCAP_ERROR_PERM_DENIED || - status == PCAP_ERROR_PROMISC_PERM_DENIED) - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s (%.*s)", device, - pcap_statustostr(status), PCAP_ERRBUF_SIZE - 6, p->errbuf); - else - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s", device, + status == PCAP_ERROR_PROMISC_PERM_DENIED) { + /* + * Only show the additional message if it's not + * empty. + */ + if (p->errbuf[0] != '\0') { + /* + * Idem. + */ + char trimbuf[PCAP_ERRBUF_SIZE - 8]; /* 2 bytes shorter */ + + pcap_strlcpy(trimbuf, p->errbuf, sizeof(trimbuf)); + snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s (%.*s)", + device, pcap_statustostr(status), + PCAP_ERRBUF_SIZE - 6, trimbuf); + } else { + snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s", + device, pcap_statustostr(status)); + } + } else { + snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s", device, pcap_statustostr(status)); + } pcap_close(p); return (NULL); } pcap_t * -pcap_open_offline_common(char *ebuf, size_t size) +pcap_open_offline_common(char *ebuf, size_t total_size, size_t private_offset) { pcap_t *p; - p = pcap_alloc_pcap_t(ebuf, size); + p = pcap_alloc_pcap_t(ebuf, total_size, private_offset); if (p == NULL) return (NULL); @@ -2725,7 +2939,7 @@ pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user) void pcap_breakloop(pcap_t *p) { - p->break_loop = 1; + p->breakloop_op(p); } int @@ -2848,11 +3062,11 @@ pcap_set_datalink(pcap_t *p, int dlt) unsupported: dlt_name = pcap_datalink_val_to_name(dlt); if (dlt_name != NULL) { - (void) pcap_snprintf(p->errbuf, sizeof(p->errbuf), + (void) snprintf(p->errbuf, sizeof(p->errbuf), "%s is not one of the DLTs supported by this device", dlt_name); } else { - (void) pcap_snprintf(p->errbuf, sizeof(p->errbuf), + (void) snprintf(p->errbuf, sizeof(p->errbuf), "DLT %d is not one of the DLTs supported by this device", dlt); } @@ -2965,7 +3179,7 @@ static struct dlt_choice dlt_choices[] = { DLT_CHOICE(RAW, "Raw IP"), DLT_CHOICE(SLIP_BSDOS, "BSD/OS SLIP"), DLT_CHOICE(PPP_BSDOS, "BSD/OS PPP"), - DLT_CHOICE(ATM_CLIP, "Linux Classical IP-over-ATM"), + DLT_CHOICE(ATM_CLIP, "Linux Classical IP over ATM"), DLT_CHOICE(PPP_SERIAL, "PPP over serial"), DLT_CHOICE(PPP_ETHER, "PPPoE"), DLT_CHOICE(SYMANTEC_FIREWALL, "Symantec Firewall"), @@ -3094,6 +3308,18 @@ static struct dlt_choice dlt_choices[] = { DLT_CHOICE(ETHERNET_MPACKET, "802.3br mPackets"), DLT_CHOICE(DISPLAYPORT_AUX, "DisplayPort AUX channel monitoring data"), DLT_CHOICE(LINUX_SLL2, "Linux cooked v2"), + DLT_CHOICE(OPENVIZSLA, "OpenVizsla USB"), + DLT_CHOICE(EBHSCR, "Elektrobit High Speed Capture and Replay (EBHSCR)"), + DLT_CHOICE(VPP_DISPATCH, "VPP graph dispatch tracer"), + DLT_CHOICE(DSA_TAG_BRCM, "Broadcom tag"), + DLT_CHOICE(DSA_TAG_BRCM_PREPEND, "Broadcom tag (prepended)"), + DLT_CHOICE(IEEE802_15_4_TAP, "IEEE 802.15.4 with pseudo-header"), + DLT_CHOICE(DSA_TAG_DSA, "Marvell DSA"), + DLT_CHOICE(DSA_TAG_EDSA, "Marvell EDSA"), + DLT_CHOICE(ELEE, "ELEE lawful intercept packets"), + DLT_CHOICE(Z_WAVE_SERIAL, "Z-Wave serial frames between host and chip"), + DLT_CHOICE(USB_2_0, "USB 2.0/1.1/1.0 as transmitted over the cable"), + DLT_CHOICE(ATSC_ALP, "ATSC Link-Layer Protocol packets"), DLT_CHOICE_SENTINEL }; @@ -3143,7 +3369,7 @@ pcap_datalink_val_to_description_or_dlt(int dlt) if (description != NULL) { return description; } else { - (void)pcap_snprintf(unkbuf, sizeof(unkbuf), "DLT %u", dlt); + (void)snprintf(unkbuf, sizeof(unkbuf), "DLT %d", dlt); return unkbuf; } } @@ -3160,6 +3386,7 @@ static struct tstamp_type_choice tstamp_type_choices[] = { { "host_hiprec", "Host, high precision", PCAP_TSTAMP_HOST_HIPREC }, { "adapter", "Adapter", PCAP_TSTAMP_ADAPTER }, { "adapter_unsynced", "Adapter, not synced with system time", PCAP_TSTAMP_ADAPTER_UNSYNCED }, + { "host_hiprec_unsynced", "Host, high precision, not synced with system time", PCAP_TSTAMP_HOST_HIPREC_UNSYNCED }, { NULL, NULL, 0 } }; @@ -3245,18 +3472,33 @@ pcap_file(pcap_t *p) return (p->rfile); } +#ifdef _WIN32 int pcap_fileno(pcap_t *p) { -#ifndef _WIN32 - return (p->fd); -#else - if (p->handle != INVALID_HANDLE_VALUE) - return ((int)(DWORD)p->handle); - else + if (p->handle != INVALID_HANDLE_VALUE) { + /* + * This is a bogus and now-deprecated API; we + * squelch the narrowing warning for the cast + * from HANDLE to intptr_t. If Windows programmmers + * need to get at the HANDLE for a pcap_t, *if* + * there is one, they should request such a + * routine (and be prepared for it to return + * INVALID_HANDLE_VALUE). + */ +DIAG_OFF_NARROWING + return ((int)(intptr_t)p->handle); +DIAG_ON_NARROWING + } else return (PCAP_ERROR); -#endif } +#else /* _WIN32 */ +int +pcap_fileno(pcap_t *p) +{ + return (p->fd); +} +#endif /* _WIN32 */ #if !defined(_WIN32) && !defined(MSDOS) int @@ -3265,7 +3507,7 @@ pcap_get_selectable_fd(pcap_t *p) return (p->selectable_fd); } -struct timeval * +const struct timeval * pcap_get_required_select_timeout(pcap_t *p) { return (p->required_select_timeout); @@ -3421,7 +3663,7 @@ pcap_statustostr(int errnum) return ("That operation is supported only in monitor mode"); case PCAP_ERROR_PERM_DENIED: - return ("You don't have permission to capture on that device"); + return ("You don't have permission to perform this capture on that device"); case PCAP_ERROR_IFACE_NOT_UP: return ("That device is not up"); @@ -3435,7 +3677,7 @@ pcap_statustostr(int errnum) case PCAP_ERROR_TSTAMP_PRECISION_NOTSUP: return ("That device doesn't support that time stamp precision"); } - (void)pcap_snprintf(ebuf, sizeof ebuf, "Unknown error: %d", errnum); + (void)snprintf(ebuf, sizeof ebuf, "Unknown error: %d", errnum); return(ebuf); } @@ -3463,7 +3705,7 @@ pcap_strerror(int errnum) if ((unsigned int)errnum < sys_nerr) return ((char *)sys_errlist[errnum]); - (void)pcap_snprintf(errbuf, sizeof errbuf, "Unknown error: %d", errnum); + (void)snprintf(errbuf, sizeof errbuf, "Unknown error: %d", errnum); return (errbuf); #endif } @@ -3484,11 +3726,29 @@ int pcap_setdirection(pcap_t *p, pcap_direction_t d) { if (p->setdirection_op == NULL) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "Setting direction is not implemented on this platform"); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Setting direction is not supported on this device"); return (-1); - } else - return (p->setdirection_op(p, d)); + } else { + switch (d) { + + case PCAP_D_IN: + case PCAP_D_OUT: + case PCAP_D_INOUT: + /* + * Valid direction. + */ + return (p->setdirection_op(p, d)); + + default: + /* + * Invalid direction. + */ + snprintf(p->errbuf, sizeof(p->errbuf), + "Invalid direction"); + return (-1); + } + } } int @@ -3620,7 +3880,7 @@ pcap_get_airpcap_handle(pcap_t *p) handle = p->get_airpcap_handle_op(p); if (handle == NULL) { - (void)pcap_snprintf(p->errbuf, sizeof(p->errbuf), + (void)snprintf(p->errbuf, sizeof(p->errbuf), "This isn't an AirPcap device"); } return (handle); @@ -3657,8 +3917,28 @@ pcap_close_all(void) { struct pcap *handle; - while ((handle = pcaps_to_close) != NULL) + while ((handle = pcaps_to_close) != NULL) { pcap_close(handle); + + /* + * If a pcap module adds a pcap_t to the "close all" + * list by calling pcap_add_to_pcaps_to_close(), it + * must have a cleanup routine that removes it from the + * list, by calling pcap_remove_from_pcaps_to_close(), + * and must make that cleanup routine the cleanup_op + * for the pcap_t. + * + * That means that, after pcap_close() - which calls + * the cleanup_op for the pcap_t - the pcap_t must + * have been removed from the list, so pcaps_to_close + * must not be equal to handle. + * + * We check for that, and abort if handle is still + * at the head of the list, to prevent infinite loops. + */ + if (pcaps_to_close == handle) + abort(); + } } int @@ -3715,9 +3995,20 @@ pcap_remove_from_pcaps_to_close(pcap_t *p) } } +void +pcap_breakloop_common(pcap_t *p) +{ + p->break_loop = 1; +} + + void pcap_cleanup_live_common(pcap_t *p) { + if (p->opt.device != NULL) { + free(p->opt.device); + p->opt.device = NULL; + } if (p->buffer != NULL) { free(p->buffer); p->buffer = NULL; @@ -3756,6 +4047,12 @@ pcap_cleanup_live_common(pcap_t *p) int pcap_sendpacket(pcap_t *p, const u_char *buf, int size) { + if (size <= 0) { + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "The number of bytes to be sent must be positive"); + return (PCAP_ERROR); + } + if (p->inject_op(p, buf, size) == -1) return (-1); return (0); @@ -3768,18 +4065,107 @@ pcap_sendpacket(pcap_t *p, const u_char *buf, int size) int pcap_inject(pcap_t *p, const void *buf, size_t size) { - return (p->inject_op(p, buf, size)); + /* + * We return the number of bytes written, so the number of + * bytes to write must fit in an int. + */ + if (size > INT_MAX) { + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "More than %d bytes cannot be injected", INT_MAX); + return (PCAP_ERROR); + } + + if (size == 0) { + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "The number of bytes to be injected must not be zero"); + return (PCAP_ERROR); + } + + return (p->inject_op(p, buf, (int)size)); } void pcap_close(pcap_t *p) { - if (p->opt.device != NULL) - free(p->opt.device); p->cleanup_op(p); free(p); } +/* + * Helpers for safely loading code at run time. + * Currently Windows-only. + */ +#ifdef _WIN32 +// +// This wrapper around loadlibrary appends the system folder (usually +// C:\Windows\System32) to the relative path of the DLL, so that the DLL +// is always loaded from an absolute path (it's no longer possible to +// load modules from the application folder). +// This solves the DLL Hijacking issue discovered in August 2010: +// +// https://blog.rapid7.com/2010/08/23/exploiting-dll-hijacking-flaws/ +// https://blog.rapid7.com/2010/08/23/application-dll-load-hijacking/ +// (the purported Rapid7 blog post link in the first of those two links +// is broken; the second of those links works.) +// +// If any links there are broken from all the content shuffling Rapid& +// did, see archived versions of the posts at their original homes, at +// +// https://web.archive.org/web/20110122175058/http://blog.metasploit.com/2010/08/exploiting-dll-hijacking-flaws.html +// https://web.archive.org/web/20100828112111/http://blog.rapid7.com/?p=5325 +// +pcap_code_handle_t +pcap_load_code(const char *name) +{ + /* + * XXX - should this work in UTF-16LE rather than in the local + * ANSI code page? + */ + CHAR path[MAX_PATH]; + CHAR fullFileName[MAX_PATH]; + UINT res; + HMODULE hModule = NULL; + + do + { + res = GetSystemDirectoryA(path, MAX_PATH); + + if (res == 0) { + // + // some bad failure occurred; + // + break; + } + + if (res > MAX_PATH) { + // + // the buffer was not big enough + // + SetLastError(ERROR_INSUFFICIENT_BUFFER); + break; + } + + if (res + 1 + strlen(name) + 1 < MAX_PATH) { + memcpy(fullFileName, path, res * sizeof(TCHAR)); + fullFileName[res] = '\\'; + memcpy(&fullFileName[res + 1], name, (strlen(name) + 1) * sizeof(TCHAR)); + + hModule = LoadLibraryA(fullFileName); + } else + SetLastError(ERROR_INSUFFICIENT_BUFFER); + + } while(FALSE); + + return hModule; +} + +pcap_funcptr_t +pcap_find_function(pcap_code_handle_t code, const char *func) +{ + return (GetProcAddress(code, func)); +} +#endif + /* * Given a BPF program, a pcap_pkthdr structure for a packet, and the raw * data for the packet, check whether the packet passes the filter. @@ -3793,7 +4179,7 @@ pcap_offline_filter(const struct bpf_program *fp, const struct pcap_pkthdr *h, const struct bpf_insn *fcode = fp->bf_insns; if (fcode != NULL) - return (bpf_filter(fcode, pkt, h->len, h->caplen)); + return (pcap_filter(fcode, pkt, h->len, h->caplen)); else return (0); } @@ -3801,7 +4187,7 @@ pcap_offline_filter(const struct bpf_program *fp, const struct pcap_pkthdr *h, static int pcap_can_set_rfmon_dead(pcap_t *p) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Rfmon mode doesn't apply on a pcap_open_dead pcap_t"); return (PCAP_ERROR); } @@ -3810,15 +4196,29 @@ static int pcap_read_dead(pcap_t *p, int cnt _U_, pcap_handler callback _U_, u_char *user _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Packets aren't available from a pcap_open_dead pcap_t"); return (-1); } -static int -pcap_inject_dead(pcap_t *p, const void *buf _U_, size_t size _U_) +static void +pcap_breakloop_dead(pcap_t *p _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + /* + * A "dead" pcap_t is just a placeholder to use in order to + * compile a filter to BPF code or to open a savefile for + * writing. It doesn't support any operations, including + * capturing or reading packets, so there will never be a + * get-packets loop in progress to break out *of*. + * + * As such, this routine doesn't need to do anything. + */ +} + +static int +pcap_inject_dead(pcap_t *p, const void *buf _U_, int size _U_) +{ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Packets can't be sent on a pcap_open_dead pcap_t"); return (-1); } @@ -3826,7 +4226,7 @@ pcap_inject_dead(pcap_t *p, const void *buf _U_, size_t size _U_) static int pcap_setfilter_dead(pcap_t *p, struct bpf_program *fp _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "A filter cannot be set on a pcap_open_dead pcap_t"); return (-1); } @@ -3834,7 +4234,7 @@ pcap_setfilter_dead(pcap_t *p, struct bpf_program *fp _U_) static int pcap_setdirection_dead(pcap_t *p, pcap_direction_t d _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "The packet direction cannot be set on a pcap_open_dead pcap_t"); return (-1); } @@ -3842,7 +4242,7 @@ pcap_setdirection_dead(pcap_t *p, pcap_direction_t d _U_) static int pcap_set_datalink_dead(pcap_t *p, int dlt _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "The link-layer header type cannot be set on a pcap_open_dead pcap_t"); return (-1); } @@ -3850,7 +4250,7 @@ pcap_set_datalink_dead(pcap_t *p, int dlt _U_) static int pcap_getnonblock_dead(pcap_t *p) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "A pcap_open_dead pcap_t does not have a non-blocking mode setting"); return (-1); } @@ -3858,7 +4258,7 @@ pcap_getnonblock_dead(pcap_t *p) static int pcap_setnonblock_dead(pcap_t *p, int nonblock _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "A pcap_open_dead pcap_t does not have a non-blocking mode setting"); return (-1); } @@ -3866,40 +4266,40 @@ pcap_setnonblock_dead(pcap_t *p, int nonblock _U_) static int pcap_stats_dead(pcap_t *p, struct pcap_stat *ps _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Statistics aren't available from a pcap_open_dead pcap_t"); return (-1); } #ifdef _WIN32 -struct pcap_stat * +static struct pcap_stat * pcap_stats_ex_dead(pcap_t *p, int *pcap_stat_size _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Statistics aren't available from a pcap_open_dead pcap_t"); return (NULL); } static int -pcap_setbuff_dead(pcap_t *p, int dim) +pcap_setbuff_dead(pcap_t *p, int dim _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "The kernel buffer size cannot be set on a pcap_open_dead pcap_t"); return (-1); } static int -pcap_setmode_dead(pcap_t *p, int mode) +pcap_setmode_dead(pcap_t *p, int mode _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "impossible to set mode on a pcap_open_dead pcap_t"); return (-1); } static int -pcap_setmintocopy_dead(pcap_t *p, int size) +pcap_setmintocopy_dead(pcap_t *p, int size _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "The mintocopy parameter cannot be set on a pcap_open_dead pcap_t"); return (-1); } @@ -3907,7 +4307,7 @@ pcap_setmintocopy_dead(pcap_t *p, int size) static HANDLE pcap_getevent_dead(pcap_t *p) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "A pcap_open_dead pcap_t has no event handle"); return (INVALID_HANDLE_VALUE); } @@ -3916,7 +4316,7 @@ static int pcap_oid_get_request_dead(pcap_t *p, bpf_u_int32 oid _U_, void *data _U_, size_t *lenp _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "An OID get request cannot be performed on a pcap_open_dead pcap_t"); return (PCAP_ERROR); } @@ -3925,45 +4325,47 @@ static int pcap_oid_set_request_dead(pcap_t *p, bpf_u_int32 oid _U_, const void *data _U_, size_t *lenp _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "An OID set request cannot be performed on a pcap_open_dead pcap_t"); return (PCAP_ERROR); } static u_int -pcap_sendqueue_transmit_dead(pcap_t *p, pcap_send_queue *queue, int sync) +pcap_sendqueue_transmit_dead(pcap_t *p, pcap_send_queue *queue _U_, + int sync _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Packets cannot be transmitted on a pcap_open_dead pcap_t"); return (0); } static int -pcap_setuserbuffer_dead(pcap_t *p, int size) +pcap_setuserbuffer_dead(pcap_t *p, int size _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "The user buffer cannot be set on a pcap_open_dead pcap_t"); return (-1); } static int -pcap_live_dump_dead(pcap_t *p, char *filename, int maxsize, int maxpacks) +pcap_live_dump_dead(pcap_t *p, char *filename _U_, int maxsize _U_, + int maxpacks _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Live packet dumping cannot be performed on a pcap_open_dead pcap_t"); return (-1); } static int -pcap_live_dump_ended_dead(pcap_t *p, int sync) +pcap_live_dump_ended_dead(pcap_t *p, int sync _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Live packet dumping cannot be performed on a pcap_open_dead pcap_t"); return (-1); } static PAirpcapHandle -pcap_get_airpcap_handle_dead(pcap_t *p) +pcap_get_airpcap_handle_dead(pcap_t *p _U_) { return (NULL); } @@ -4026,6 +4428,7 @@ pcap_open_dead_with_tstamp_precision(int linktype, int snaplen, u_int precision) p->live_dump_ended_op = pcap_live_dump_ended_dead; p->get_airpcap_handle_op = pcap_get_airpcap_handle_dead; #endif + p->breakloop_op = pcap_breakloop_dead; p->cleanup_op = pcap_cleanup_dead; /* diff --git a/pcap/bpf.h b/pcap/bpf.h index 9d748952a0e5..a8eb177b43bc 100644 --- a/pcap/bpf.h +++ b/pcap/bpf.h @@ -58,10 +58,21 @@ * I don't have earlier versions available to check), or QNX-style * multiple-include protection (as per GitHub pull request #394). * + * We trust that they will define structures and macros and types in + * a fashion that's source-compatible and binary-compatible with our + * definitions. + * * We do not check for BPF_MAJOR_VERSION, as that's defined by * , which is directly or indirectly included in some * programs that also include pcap.h, and doesn't - * define stuff we need. + * define stuff we need. We *do* protect against + * defining various macros for BPF code itself; says + * + * Try and keep these values and structures similar to BSD, especially + * the BPF code definitions which need to match so you can share filters + * + * so we trust that it will define them in a fashion that's source-compatible + * and binary-compatible with our definitions. * * This also provides our own multiple-include protection. */ @@ -69,6 +80,7 @@ #define lib_pcap_bpf_h #include +#include #ifdef __cplusplus extern "C" { @@ -106,8 +118,6 @@ struct bpf_program { struct bpf_insn *bf_insns; }; -#include - /* * The instruction encodings. * @@ -240,12 +250,34 @@ struct bpf_insn { /* * Macros for insn array initializers. + * + * In case somebody's included , or something else that + * gives the kernel's definitions of BPF statements, get rid of its + * definitions, so we can supply ours instead. If some kernel's + * definitions aren't *binary-compatible* with what BPF has had + * since it first sprung from the brows of Van Jacobson and Steve + * McCanne, that kernel should be fixed. */ +#ifdef BPF_STMT +#undef BPF_STMT +#endif #define BPF_STMT(code, k) { (u_short)(code), 0, 0, k } +#ifdef BPF_JUMP +#undef BPF_JUMP +#endif #define BPF_JUMP(code, k, jt, jf) { (u_short)(code), jt, jf, k } -PCAP_API int bpf_validate(const struct bpf_insn *, int); -PCAP_API u_int bpf_filter(const struct bpf_insn *, const u_char *, u_int, u_int); +PCAP_AVAILABLE_0_4 +PCAP_API u_int bpf_filter(const struct bpf_insn *, const u_char *, u_int, u_int); + +PCAP_AVAILABLE_0_6 +PCAP_API int bpf_validate(const struct bpf_insn *f, int len); + +PCAP_AVAILABLE_0_4 +PCAP_API char *bpf_image(const struct bpf_insn *, int); + +PCAP_AVAILABLE_0_6 +PCAP_API void bpf_dump(const struct bpf_program *, int); /* * Number of scratch memory words (for BPF_LD|BPF_MEM and BPF_ST). diff --git a/pcap/can_socketcan.h b/pcap/can_socketcan.h index 332d9ff5d5a9..0cb3584a8497 100644 --- a/pcap/can_socketcan.h +++ b/pcap/can_socketcan.h @@ -48,9 +48,14 @@ typedef struct { uint32_t can_id; uint8_t payload_length; - uint8_t pad; + uint8_t fd_flags; uint8_t reserved1; uint8_t reserved2; } pcap_can_socketcan_hdr; +/* Bits in the fd_flags field */ +#define CANFD_BRS 0x01 /* bit rate switch (second bitrate for payload data) */ +#define CANFD_ESI 0x02 /* error state indicator of the transmitting node */ +#define CANFD_FDF 0x04 /* mark CAN FD for dual use of CAN format */ + #endif diff --git a/pcap/compiler-tests.h b/pcap/compiler-tests.h index ea5962c7bf5e..2d98a7070511 100644 --- a/pcap/compiler-tests.h +++ b/pcap/compiler-tests.h @@ -38,7 +38,7 @@ /* * This was introduced by Clang: * - * http://clang.llvm.org/docs/LanguageExtensions.html#has-attribute + * https://clang.llvm.org/docs/LanguageExtensions.html#has-attribute * * in some version (which version?); it has been picked up by GCC 5.0. */ @@ -80,9 +80,11 @@ */ #if ! defined(__GNUC__) -#define PCAP_IS_AT_LEAST_GNUC_VERSION(major, minor) 0 + /* Not GCC and not "just like GCC" */ + #define PCAP_IS_AT_LEAST_GNUC_VERSION(major, minor) 0 #else -#define PCAP_IS_AT_LEAST_GNUC_VERSION(major, minor) \ + /* GCC or "just like GCC" */ + #define PCAP_IS_AT_LEAST_GNUC_VERSION(major, minor) \ (__GNUC__ > (major) || \ (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor))) #endif @@ -92,9 +94,11 @@ */ #if !defined(__clang__) -#define PCAP_IS_AT_LEAST_CLANG_VERSION(major, minor) 0 + /* Not Clang */ + #define PCAP_IS_AT_LEAST_CLANG_VERSION(major, minor) 0 #else -#define PCAP_IS_AT_LEAST_CLANG_VERSION(major, minor) \ + /* Clang */ + #define PCAP_IS_AT_LEAST_CLANG_VERSION(major, minor) \ (__clang_major__ > (major) || \ (__clang_major__ == (major) && __clang_minor__ >= (minor))) #endif @@ -118,13 +122,15 @@ */ #if ! defined(__SUNPRO_C) -#define PCAP_IS_AT_LEAST_SUNC_VERSION(major,minor) 0 + /* Not Sun/Oracle C */ + #define PCAP_IS_AT_LEAST_SUNC_VERSION(major,minor) 0 #else -#define PCAP_SUNPRO_VERSION_TO_BCD(major, minor) \ + /* Sun/Oracle C */ + #define PCAP_SUNPRO_VERSION_TO_BCD(major, minor) \ (((minor) >= 10) ? \ (((major) << 12) | (((minor)/10) << 8) | (((minor)%10) << 4)) : \ (((major) << 8) | ((minor) << 4))) -#define PCAP_IS_AT_LEAST_SUNC_VERSION(major,minor) \ + #define PCAP_IS_AT_LEAST_SUNC_VERSION(major,minor) \ (__SUNPRO_C >= PCAP_SUNPRO_VERSION_TO_BCD((major), (minor))) #endif @@ -133,13 +139,31 @@ * * The version number in __xlC__ has the major version in the * upper 8 bits and the minor version in the lower 8 bits. + * On AIX __xlC__ is always defined, __ibmxl__ becomes defined in XL C 16.1. + * On Linux since XL C 13.1.6 __xlC__ is not defined by default anymore, but + * __ibmxl__ is defined since at least XL C 13.1.1. */ -#if ! defined(__xlC__) -#define PCAP_IS_AT_LEAST_XL_C_VERSION(major,minor) 0 +#if ! defined(__xlC__) && ! defined(__ibmxl__) + /* Not XL C */ + #define PCAP_IS_AT_LEAST_XL_C_VERSION(major,minor) 0 #else -#define PCAP_IS_AT_LEAST_XL_C_VERSION(major, minor) \ + /* XL C */ + #if defined(__ibmxl__) + /* + * Later Linux version of XL C; use __ibmxl_version__ to test + * the version. + */ + #define PCAP_IS_AT_LEAST_XL_C_VERSION(major, minor) \ + (__ibmxl_version__ > (major) || \ + (__ibmxl_version__ == (major) && __ibmxl_release__ >= (minor))) + #else /* __ibmxl__ */ + /* + * __ibmxl__ not defined; use __xlC__ to test the version. + */ + #define PCAP_IS_AT_LEAST_XL_C_VERSION(major, minor) \ (__xlC__ >= (((major) << 8) | (minor))) + #endif /* __ibmxl__ */ #endif /* @@ -154,9 +178,11 @@ */ #if ! defined(__HP_aCC) -#define PCAP_IS_AT_LEAST_HP_C_VERSION(major,minor) 0 + /* Not HP C */ + #define PCAP_IS_AT_LEAST_HP_C_VERSION(major,minor) 0 #else -#define PCAP_IS_AT_LEAST_HP_C_VERSION(major,minor) \ + /* HP C */ + #define PCAP_IS_AT_LEAST_HP_C_VERSION(major,minor) \ (__HP_aCC >= ((major)*10000 + (minor)*100)) #endif diff --git a/pcap/dlt.h b/pcap/dlt.h index 8dacf024044c..ea8a5ba0d8ba 100644 --- a/pcap/dlt.h +++ b/pcap/dlt.h @@ -104,6 +104,67 @@ #define DLT_PPP_BSDOS 16 /* BSD/OS Point-to-point Protocol */ #endif +/* + * NetBSD uses 15 for HIPPI. + * + * From a quick look at sys/net/if_hippi.h and sys/net/if_hippisubr.c + * in an older version of NetBSD , the header appears to be: + * + * a 1-byte ULP field (ULP-id)? + * + * a 1-byte flags field; + * + * a 2-byte "offsets" field; + * + * a 4-byte "D2 length" field (D2_Size?); + * + * a 4-byte "destination switch" field (or a 1-byte field + * containing the Forwarding Class, Double_Wide, and Message_Type + * sub fields, followed by a 3-byte Destination_Switch_Address + * field?, HIPPI-LE 3.4-style?); + * + * a 4-byte "source switch" field (or a 1-byte field containing the + * Destination_Address_type and Source_Address_Type fields, followed + * by a 3-byte Source_Switch_Address field, HIPPI-LE 3.4-style?); + * + * a 2-byte reserved field; + * + * a 6-byte destination address field; + * + * a 2-byte "local admin" field; + * + * a 6-byte source address field; + * + * followed by an 802.2 LLC header. + * + * This looks somewhat like something derived from the HIPPI-FP 4.4 + * Header_Area, followed an HIPPI-FP 4.4 D1_Area containing a D1 data set + * with the header in HIPPI-LE 3.4 (ANSI X3.218-1993), followed by an + * HIPPI-FP 4.4 D2_Area (with no Offset) containing the 802.2 LLC header + * and payload? Or does the "offsets" field contain the D2_Offset, + * with that many bytes of offset before the payload? + * + * See http://wotug.org/parallel/standards/hippi/ for an archive of + * HIPPI specifications. + * + * RFC 2067 imposes some additional restrictions. It says that the + * Offset is always zero + * + * HIPPI is long-gone, and the source files found in an older version + * of NetBSD don't appear to be in the main CVS branch, so we may never + * see a capture with this link-layer type. + */ +#if defined(__NetBSD__) +#define DLT_HIPPI 15 /* HIPPI */ +#endif + +/* + * NetBSD uses 16 for DLT_HDLC; see below. + * BSD/OS uses it for PPP; see above. + * As far as I know, no other OS uses it for anything; don't use it + * for anything else. + */ + /* * 17 was used for DLT_PFLOG in OpenBSD; it no longer is. * @@ -136,7 +197,7 @@ #define DLT_PFSYNC 18 #endif -#define DLT_ATM_CLIP 19 /* Linux Classical-IP over ATM */ +#define DLT_ATM_CLIP 19 /* Linux Classical IP over ATM */ /* * Apparently Redback uses this for its SmartEdge 400/800. I hope @@ -219,7 +280,8 @@ * that the AF_ type in the link-layer header is in network byte order. * * DLT_LOOP is 12 in OpenBSD, but that's DLT_RAW in other OSes, so - * we don't use 12 for it in OSes other than OpenBSD. + * we don't use 12 for it in OSes other than OpenBSD; instead, we + * use the same value as LINKTYPE_LOOP. */ #ifdef __OpenBSD__ #define DLT_LOOP 12 @@ -230,7 +292,7 @@ /* * Encapsulated packets for IPsec; DLT_ENC is 13 in OpenBSD, but that's * DLT_SLIP_BSDOS in NetBSD, so we don't use 13 for it in OSes other - * than OpenBSD. + * than OpenBSD; instead, we use the same value as LINKTYPE_ENC. */ #ifdef __OpenBSD__ #define DLT_ENC 13 @@ -239,12 +301,22 @@ #endif /* - * Values between 110 and 112 are reserved for use in capture file headers + * Values 110 and 111 are reserved for use in capture file headers * as link-layer types corresponding to DLT_ types that might differ * between platforms; don't use those values for new DLT_ types * other than the corresponding DLT_ types. */ +/* + * NetBSD uses 16 for (Cisco) "HDLC framing". For other platforms, + * we define it to have the same value as LINKTYPE_NETBSD_HDLC. + */ +#if defined(__NetBSD__) +#define DLT_HDLC 16 /* Cisco HDLC */ +#else +#define DLT_HDLC 112 +#endif + /* * Linux cooked sockets. */ @@ -465,7 +537,7 @@ #define DLT_DOCSIS 143 /* - * Linux-IrDA packets. Protocol defined at http://www.irda.org. + * Linux-IrDA packets. Protocol defined at https://www.irda.org. * Those packets include IrLAP headers and above (IrLMP...), but * don't include Phy framing (SOF/EOF/CRC & byte stuffing), because Phy * framing can be handled by the hardware and depend on the bitrate. @@ -473,7 +545,7 @@ * interface (irdaX), but not on a raw serial port. * Note the capture is done in "Linux-cooked" mode, so each packet include * a fake packet header (struct sll_header). This is because IrDA packet - * decoding is dependant on the direction of the packet (incomming or + * decoding is dependent on the direction of the packet (incoming or * outgoing). * When/if other platform implement IrDA capture, we may revisit the * issue and define a real DLT_IRDA... @@ -565,7 +637,7 @@ * input packets such as port scans, packets from old lost connections, * etc. to force the connection to stay up). * - * The first byte of the PPP header (0xff03) is modified to accomodate + * The first byte of the PPP header (0xff03) is modified to accommodate * the direction - 0x00 = IN, 0x01 = OUT. */ #define DLT_PPP_PPPD 166 @@ -607,7 +679,7 @@ /* * Link types requested by Gregor Maier of Endace * Measurement Systems. They add an ERF header (see - * http://www.endace.com/support/EndaceRecordFormat.pdf) in front of + * https://www.endace.com/support/EndaceRecordFormat.pdf) in front of * the link-layer header. */ #define DLT_ERF_ETH 175 /* Ethernet */ @@ -651,7 +723,7 @@ * DLT_ requested by Gianluca Varenni . * Every frame contains a 32bit A429 label. * More documentation on Arinc 429 can be found at - * http://www.condoreng.com/support/downloads/tutorials/ARINCTutorial.pdf + * https://web.archive.org/web/20040616233302/https://www.condoreng.com/support/downloads/tutorials/ARINCTutorial.pdf */ #define DLT_A429 184 @@ -750,7 +822,7 @@ /* * Various link-layer types, with a pseudo-header, for SITA - * (http://www.sita.aero/); requested by Fulko Hew (fulko.hew@gmail.com). + * (https://www.sita.aero/); requested by Fulko Hew (fulko.hew@gmail.com). */ #define DLT_SITA 196 @@ -817,8 +889,11 @@ * PPP, with a one-byte direction pseudo-header prepended - zero means * "received by this host", non-zero (any non-zero value) means "sent by * this host" - as per Will Barker . + * + * Don't confuse this with DLT_PPP_WITH_DIRECTION, which is an old + * name for what is now called DLT_PPP_PPPD. */ -#define DLT_PPP_WITH_DIR 204 /* Don't confuse with DLT_PPP_WITH_DIRECTION */ +#define DLT_PPP_WITH_DIR 204 /* * Cisco HDLC, with a one-byte direction pseudo-header prepended - zero @@ -862,7 +937,7 @@ /* * Media Oriented Systems Transport (MOST) bus for multimedia - * transport - http://www.mostcooperation.com/ - as requested + * transport - https://www.mostcooperation.com/ - as requested * by Hannes Kaelber . */ #define DLT_MOST 211 @@ -1048,16 +1123,16 @@ /* * Raw D-Bus: * - * http://www.freedesktop.org/wiki/Software/dbus + * https://www.freedesktop.org/wiki/Software/dbus * * messages: * - * http://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-messages + * https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-messages * * starting with the endianness flag, followed by the message type, etc., * but without the authentication handshake before the message sequence: * - * http://dbus.freedesktop.org/doc/dbus-specification.html#auth-protocol + * https://dbus.freedesktop.org/doc/dbus-specification.html#auth-protocol * * Requested by Martin Vidner . */ @@ -1075,7 +1150,7 @@ * DVB-CI (DVB Common Interface for communication between a PC Card * module and a DVB receiver). See * - * http://www.kaiser.cx/pcap-dvbci.html + * https://www.kaiser.cx/pcap-dvbci.html * * for the specification. * @@ -1211,15 +1286,17 @@ #define DLT_BLUETOOTH_LE_LL 251 /* - * DLT type for upper-protocol layer PDU saves from wireshark. + * DLT type for upper-protocol layer PDU saves from Wireshark. * - * the actual contents are determined by two TAGs stored with each - * packet: - * EXP_PDU_TAG_LINKTYPE the link type (LINKTYPE_ value) of the - * original packet. + * the actual contents are determined by two TAGs, one or more of + * which is stored with each packet: * - * EXP_PDU_TAG_PROTO_NAME the name of the wireshark dissector - * that can make sense of the data stored. + * EXP_PDU_TAG_DISSECTOR_NAME the name of the Wireshark dissector + * that can make sense of the data stored. + * + * EXP_PDU_TAG_HEUR_DISSECTOR_NAME the name of the Wireshark heuristic + * dissector that can make sense of the + * data stored. */ #define DLT_WIRESHARK_UPPER_PDU 252 @@ -1364,9 +1441,9 @@ /* * per: Stefanha at gmail.com for - * http://lists.sandelman.ca/pipermail/tcpdump-workers/2017-May/000772.html + * https://lists.sandelman.ca/pipermail/tcpdump-workers/2017-May/000772.html * and: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/vsockmon.h - * for: http://qemu-project.org/Features/VirtioVsock + * for: https://qemu-project.org/Features/VirtioVsock */ #define DLT_VSOCK 271 @@ -1378,7 +1455,7 @@ /* * Excentis DOCSIS 3.1 RF sniffer (XRA-31) * per: bruno.verstuyft at excentis.com - * http://www.xra31.com/xra-header + * https://www.xra31.com/xra-header */ #define DLT_DOCSIS31_XRA31 273 @@ -1390,7 +1467,7 @@ /* * DisplayPort AUX channel monitoring data as specified by VESA - * DisplayPort(DP) Standard preceeded by a pseudo-header. + * DisplayPort(DP) Standard preceded by a pseudo-header. * per dirk.eibach at gdsys.cc */ #define DLT_DISPLAYPORT_AUX 275 @@ -1400,6 +1477,83 @@ */ #define DLT_LINUX_SLL2 276 +/* + * Sercos Monitor, per Manuel Jacob + */ +#define DLT_SERCOS_MONITOR 277 + +/* + * OpenVizsla http://openvizsla.org is open source USB analyzer hardware. + * It consists of FPGA with attached USB phy and FTDI chip for streaming + * the data to the host PC. + * + * Current OpenVizsla data encapsulation format is described here: + * https://github.com/matwey/libopenvizsla/wiki/OpenVizsla-protocol-description + * + */ +#define DLT_OPENVIZSLA 278 + +/* + * The Elektrobit High Speed Capture and Replay (EBHSCR) protocol is produced + * by a PCIe Card for interfacing high speed automotive interfaces. + * + * The specification for this frame format can be found at: + * https://www.elektrobit.com/ebhscr + * + * for Guenter.Ebermann at elektrobit.com + * + */ +#define DLT_EBHSCR 279 + +/* + * The https://fd.io vpp graph dispatch tracer produces pcap trace files + * in the format documented here: + * https://fdio-vpp.readthedocs.io/en/latest/gettingstarted/developers/vnet.html#graph-dispatcher-pcap-tracing + */ +#define DLT_VPP_DISPATCH 280 + +/* + * Broadcom Ethernet switches (ROBO switch) 4 bytes proprietary tagging format. + */ +#define DLT_DSA_TAG_BRCM 281 +#define DLT_DSA_TAG_BRCM_PREPEND 282 + +/* + * IEEE 802.15.4 with pseudo-header and optional meta-data TLVs, PHY payload + * exactly as it appears in the spec (no padding, no nothing), and FCS if + * specified by FCS Type TLV; requested by James Ko . + * Specification at https://github.com/jkcko/ieee802.15.4-tap + */ +#define DLT_IEEE802_15_4_TAP 283 + +/* + * Marvell (Ethertype) Distributed Switch Architecture proprietary tagging format. + */ +#define DLT_DSA_TAG_DSA 284 +#define DLT_DSA_TAG_EDSA 285 + +/* + * Payload of lawful intercept packets using the ELEE protocol; + * https://socket.hr/draft-dfranusic-opsawg-elee-00.xml + * https://xml2rfc.tools.ietf.org/cgi-bin/xml2rfc.cgi?url=https://socket.hr/draft-dfranusic-opsawg-elee-00.xml&modeAsFormat=html/ascii + */ +#define DLT_ELEE 286 + +/* + * Serial frames transmitted between a host and a Z-Wave chip. + */ +#define DLT_Z_WAVE_SERIAL 287 + +/* + * USB 2.0, 1.1, and 1.0 packets as transmitted over the cable. + */ +#define DLT_USB_2_0 288 + +/* + * ATSC Link-Layer Protocol (A/330) packets. + */ +#define DLT_ATSC_ALP 289 + /* * In case the code that includes this file (directly or indirectly) * has also included OS files that happen to define DLT_MATCHING_MAX, @@ -1410,7 +1564,7 @@ #ifdef DLT_MATCHING_MAX #undef DLT_MATCHING_MAX #endif -#define DLT_MATCHING_MAX 276 /* highest value in the "matching" range */ +#define DLT_MATCHING_MAX 289 /* highest value in the "matching" range */ /* * DLT and savefile link type values are split into a class and diff --git a/pcap/funcattrs.h b/pcap/funcattrs.h index e64da93aa4ce..374094992308 100644 --- a/pcap/funcattrs.h +++ b/pcap/funcattrs.h @@ -118,14 +118,14 @@ #if PCAP_IS_AT_LEAST_GNUC_VERSION(3,4) \ || PCAP_IS_AT_LEAST_XL_C_VERSION(12,0) /* - * GCC 3.4 or later, or some compiler asserting compatibility with - * GCC 3.4 or later, or XL C 13.0 or later, so we have + * GCC 3.4 and later, or some compiler asserting compatibility with + * GCC 3.4 and later, or XL C 13.0 and later, so we have * __attribute__((visibility()). */ #define PCAP_API_DEF __attribute__((visibility("default"))) #elif PCAP_IS_AT_LEAST_SUNC_VERSION(5,5) /* - * Sun C 5.5 or later, so we have __global. + * Sun C 5.5 and later, so we have __global. * (Sun C 5.9 and later also have __attribute__((visibility()), * but there's no reason to prefer it with Sun C.) */ @@ -146,6 +146,88 @@ #define PCAP_API PCAP_API_DEF extern +/* + * Definitions to 1) indicate what version of libpcap first had a given + * API and 2) allow upstream providers whose build environments allow + * APIs to be designated as "first available in this release" to do so + * by appropriately defining them. + * + * Yes, that's you, Apple. :-) Please define PCAP_AVAILABLE_MACOS() + * as necessary to make various APIs "weak exports" to make it easier + * for software that's distributed in binary form and that uses libpcap + * to run on multiple macOS versions and use new APIs when available. + * (Yes, such third-party software exists - Wireshark provides binary + * packages for macOS, for example. tcpdump doesn't count, as that's + * provided by Apple, so each release can come with a version compiled + * to use the APIs present in that release.) + * + * The non-macOS versioning is based on + * + * https://en.wikipedia.org/wiki/Darwin_(operating_system)#Release_history + * + * If there are any corrections, please submit it upstream to the + * libpcap maintainers, preferably as a pull request on + * + * https://github.com/the-tcpdump-group/libpcap + * + * We don't define it ourselves because, if you're building and + * installing libpcap on macOS yourself, the APIs will be available + * no matter what OS version you're installing it on. + * + * For other platforms, we don't define them, leaving it up to + * others to do so based on their OS versions, if appropriate. + * + * We start with libpcap 0.4, as that was the last LBL release, and + * I've never seen earlier releases. + */ +#ifdef __APPLE__ +#include +/* + * When building as part of macOS, define this as __API_AVAILABLE(__VA_ARGS__). + * + * XXX - if there's some #define to indicate that this is being built + * as part of the macOS build process, we could make that Just Work. + */ +#define PCAP_AVAILABLE(...) +#define PCAP_AVAILABLE_0_4 PCAP_AVAILABLE(macos(10.0)) /* Did any version of Mac OS X ship with this? */ +#define PCAP_AVAILABLE_0_5 PCAP_AVAILABLE(macos(10.0)) /* Did any version of Mac OS X ship with this? */ +#define PCAP_AVAILABLE_0_6 PCAP_AVAILABLE(macos(10.1)) +#define PCAP_AVAILABLE_0_7 PCAP_AVAILABLE(macos(10.4)) +#define PCAP_AVAILABLE_0_8 PCAP_AVAILABLE(macos(10.4)) +#define PCAP_AVAILABLE_0_9 PCAP_AVAILABLE(macos(10.5), ios(1.0)) +#define PCAP_AVAILABLE_1_0 PCAP_AVAILABLE(macos(10.6), ios(4.0)) +/* #define PCAP_AVAILABLE_1_1 no routines added to the API */ +#define PCAP_AVAILABLE_1_2 PCAP_AVAILABLE(macos(10.9), ios(6.0)) +/* #define PCAP_AVAILABLE_1_3 no routines added to the API */ +/* #define PCAP_AVAILABLE_1_4 no routines added to the API */ +#define PCAP_AVAILABLE_1_5 PCAP_AVAILABLE(macos(10.10), ios(7.0), watchos(1.0)) +/* #define PCAP_AVAILABLE_1_6 no routines added to the API */ +#define PCAP_AVAILABLE_1_7 PCAP_AVAILABLE(macos(10.12), ios(10.0), tvos(10.0), watchos(3.0)) +#define PCAP_AVAILABLE_1_8 PCAP_AVAILABLE(macos(10.13), ios(11.0), tvos(11.0), watchos(4.0)) /* only Windows adds routines to the API; XXX - what version first had it? */ +#define PCAP_AVAILABLE_1_9 PCAP_AVAILABLE(macos(10.13), ios(11.0), tvos(11.0), watchos(4.0)) +#define PCAP_AVAILABLE_1_10 /* not in macOS yet */ +#define PCAP_AVAILABLE_1_11 /* not released yet, so not in macOS yet */ +#else /* __APPLE__ */ +#define PCAP_AVAILABLE_0_4 +#define PCAP_AVAILABLE_0_5 +#define PCAP_AVAILABLE_0_6 +#define PCAP_AVAILABLE_0_7 +#define PCAP_AVAILABLE_0_8 +#define PCAP_AVAILABLE_0_9 +#define PCAP_AVAILABLE_1_0 +/* #define PCAP_AVAILABLE_1_1 no routines added to the API */ +#define PCAP_AVAILABLE_1_2 +/* #define PCAP_AVAILABLE_1_3 no routines added to the API */ +/* #define PCAP_AVAILABLE_1_4 no routines added to the API */ +#define PCAP_AVAILABLE_1_5 +/* #define PCAP_AVAILABLE_1_6 no routines added to the API */ +#define PCAP_AVAILABLE_1_7 +#define PCAP_AVAILABLE_1_8 +#define PCAP_AVAILABLE_1_9 +#define PCAP_AVAILABLE_1_10 +#define PCAP_AVAILABLE_1_11 +#endif /* __APPLE__ */ + /* * PCAP_NORETURN, before a function declaration, means "this function * never returns". (It must go before the function declaration, e.g. @@ -164,11 +246,11 @@ || PCAP_IS_AT_LEAST_XL_C_VERSION(10,1) \ || PCAP_IS_AT_LEAST_HP_C_VERSION(6,10) /* - * Compiler with support for __attribute((noreturn)), or GCC 2.5 or - * later, or some compiler asserting compatibility with GCC 2.5 or - * later, or Solaris Studio 12 (Sun C 5.9) or later, or IBM XL C 10.1 - * or later (do any earlier versions of XL C support this?), or HP aCC - * A.06.10 or later. + * Compiler with support for __attribute((noreturn)), or GCC 2.5 and + * later, or some compiler asserting compatibility with GCC 2.5 and + * later, or Solaris Studio 12 (Sun C 5.9) and later, or IBM XL C 10.1 + * and later (do any earlier versions of XL C support this?), or HP aCC + * A.06.10 and later. */ #define PCAP_NORETURN __attribute((noreturn)) #define PCAP_NORETURN_DEF __attribute((noreturn)) @@ -194,8 +276,8 @@ || PCAP_IS_AT_LEAST_XL_C_VERSION(10,1) \ || PCAP_IS_AT_LEAST_HP_C_VERSION(6,10) /* - * Compiler with support for it, or GCC 2.3 or later, or some compiler - * asserting compatibility with GCC 2.3 or later, or IBM XL C 10.1 + * Compiler with support for it, or GCC 2.3 and later, or some compiler + * asserting compatibility with GCC 2.3 and later, or IBM XL C 10.1 * and later (do any earlier versions of XL C support this?), * or HP aCC A.06.10 and later. */ @@ -208,23 +290,21 @@ * PCAP_DEPRECATED(func, msg), after a function declaration, marks the * function as deprecated. * - * The first argument is the name of the function; the second argument is - * a string giving the warning message to use if the compiler supports that. - * - * (Thank you, Microsoft, for requiring the function name.) + * The argument is a string giving the warning message to use if the + * compiler supports that. */ #if __has_attribute(deprecated) \ || PCAP_IS_AT_LEAST_GNUC_VERSION(4,5) \ || PCAP_IS_AT_LEAST_SUNC_VERSION(5,13) /* * Compiler that supports __has_attribute and __attribute__((deprecated)), - * or GCC 4.5 or later, or Sun/Oracle C 12.4 (Sun C 5.13) or later. + * or GCC 4.5 and later, or Sun/Oracle C 12.4 (Sun C 5.13) and later. * * Those support __attribute__((deprecated(msg))) (we assume, perhaps * incorrectly, that anything that supports __has_attribute() is * recent enough to support __attribute__((deprecated(msg)))). */ - #define PCAP_DEPRECATED(func, msg) __attribute__((deprecated(msg))) + #define PCAP_DEPRECATED(msg) __attribute__((deprecated(msg))) #elif PCAP_IS_AT_LEAST_GNUC_VERSION(3,1) /* * GCC 3.1 through 4.4. @@ -232,18 +312,18 @@ * Those support __attribute__((deprecated)) but not * __attribute__((deprecated(msg))). */ - #define PCAP_DEPRECATED(func, msg) __attribute__((deprecated)) -#elif (defined(_MSC_VER) && (_MSC_VER >= 1500)) && !defined(BUILDING_PCAP) + #define PCAP_DEPRECATED(msg) __attribute__((deprecated)) +#elif defined(_MSC_VER) && !defined(BUILDING_PCAP) /* - * MSVC from Visual Studio 2008 or later, and we're not building - * libpcap itself. + * MSVC, and we're not building libpcap itself; it's VS 2015 + * and later, so we have __declspec(deprecated(...)). * * If we *are* building libpcap, we don't want this, as it'll warn * us even if we *define* the function. */ - #define PCAP_DEPRECATED(func, msg) __pragma(deprecated(func)) + #define PCAP_DEPRECATED(msg) _declspec(deprecated(msg)) #else - #define PCAP_DEPRECATED(func, msg) + #define PCAP_DEPRECATED(msg) #endif /* @@ -251,11 +331,7 @@ */ #ifdef _MSC_VER #include - #if _MSC_VER > 1400 - #define PCAP_FORMAT_STRING(p) _Printf_format_string_ p - #else - #define PCAP_FORMAT_STRING(p) __format_string p - #endif + #define PCAP_FORMAT_STRING(p) _Printf_format_string_ p #else #define PCAP_FORMAT_STRING(p) p #endif diff --git a/pcap/namedb.h b/pcap/namedb.h index c66846d3451b..51d1e3184b0e 100644 --- a/pcap/namedb.h +++ b/pcap/namedb.h @@ -59,7 +59,9 @@ PCAP_API struct pcap_etherent *pcap_next_etherent(FILE *); PCAP_API u_char *pcap_ether_hostton(const char*); PCAP_API u_char *pcap_ether_aton(const char *); -PCAP_API bpf_u_int32 **pcap_nametoaddr(const char *); +PCAP_API +PCAP_DEPRECATED("this is not reentrant; use 'pcap_nametoaddrinfo' instead") +bpf_u_int32 **pcap_nametoaddr(const char *); PCAP_API struct addrinfo *pcap_nametoaddrinfo(const char *); PCAP_API bpf_u_int32 pcap_nametonetaddr(const char *); diff --git a/pcap/pcap-inttypes.h b/pcap/pcap-inttypes.h index 8b1eb8b9be84..8c7b4f651ede 100644 --- a/pcap/pcap-inttypes.h +++ b/pcap/pcap-inttypes.h @@ -32,8 +32,13 @@ #define pcap_pcap_inttypes_h /* - * Get the integer types and PRi[doux]64 values from C99 - * defined, by hook or by crook. + * If we're compiling with Visual Studio, make sure the C99 integer + * types are defined, by hook or by crook. + * + * XXX - verify that we have at least C99 support on UN*Xes? + * + * What about MinGW or various DOS toolchains? We're currently assuming + * sufficient C99 support there. */ #if defined(_MSC_VER) /* @@ -47,6 +52,10 @@ #else /* * Earlier VS; we have to define this stuff ourselves. + * We don't support building libpcap with earlier versions of VS, + * but SDKs for Npcap have to support building applications using + * earlier versions of VS, so we work around this by defining + * those types ourselves, as some files use them. */ typedef unsigned char uint8_t; typedef signed char int8_t; @@ -62,67 +71,31 @@ typedef long long int64_t; #endif #endif - +#else /* defined(_MSC_VER) */ /* - * These may be defined by . + * Not Visual Studio. + * Include to get the integer types and PRi[doux]64 values + * defined. * - * XXX - for MSVC, we always want the _MSC_EXTENSIONS versions. - * What about other compilers? If, as the MinGW Web site says MinGW - * does, the other compilers just use Microsoft's run-time library, - * then they should probably use the _MSC_EXTENSIONS even if the - * compiler doesn't define _MSC_EXTENSIONS. + * If the compiler is MinGW, we assume we have - and + * support for %zu in the formatted printing functions. * - * XXX - we currently aren't using any of these, but this allows - * their use in the future. + * If the target is UN*X, we assume we have a C99-or-later development + * environment, and thus have - and support for %zu in + * the formatted printing functions. + * + * If the target is MS-DOS, we assume we have - and support + * for %zu in the formatted printing functions. + * + * I.e., assume we have and that it suffices. */ - #ifndef PRId64 - #ifdef _MSC_EXTENSIONS - #define PRId64 "I64d" - #else - #define PRId64 "lld" - #endif - #endif /* PRId64 */ - - #ifndef PRIo64 - #ifdef _MSC_EXTENSIONS - #define PRIo64 "I64o" - #else - #define PRIo64 "llo" - #endif - #endif /* PRIo64 */ - - #ifndef PRIx64 - #ifdef _MSC_EXTENSIONS - #define PRIx64 "I64x" - #else - #define PRIx64 "llx" - #endif - #endif - - #ifndef PRIu64 - #ifdef _MSC_EXTENSIONS - #define PRIu64 "I64u" - #else - #define PRIu64 "llu" - #endif - #endif /* - * MSVC's support library doesn't support %zu to print a size_t until - * Visual Studio 2017, but supports %Iu earlier, so use that. - */ - #define PRIsize "Iu" -#elif defined(__MINGW32__) || !defined(_WIN32) - /* - * Compiler is MinGW or target is UN*X or MS-DOS. Just use - * . + * XXX - somehow make sure we have enough C99 support with other + * compilers and support libraries? */ + #include - - /* - * Assume the support library supports %zu; it's required by C99. - */ - #define PRIsize "zu" -#endif +#endif /* defined(_MSC_VER) */ #endif /* pcap/pcap-inttypes.h */ diff --git a/pcap/pcap.h b/pcap/pcap.h index 90614dd0156f..9fd14f5e9688 100644 --- a/pcap/pcap.h +++ b/pcap/pcap.h @@ -69,6 +69,49 @@ #ifndef lib_pcap_pcap_h #define lib_pcap_pcap_h +/* + * Some software that uses libpcap/WinPcap/Npcap defines _MSC_VER before + * including pcap.h if it's not defined - and it defines it to 1500. + * (I'm looking at *you*, lwIP!) + * + * Attempt to detect this, and undefine _MSC_VER so that we can *reliably* + * use it to know what compiler is being used and, if it's Visual Studio, + * what version is being used. + */ +#if defined(_MSC_VER) + /* + * We assume here that software such as that doesn't define _MSC_FULL_VER + * as well and that it defines _MSC_VER with a value > 1200. + * + * DO NOT BREAK THESE ASSUMPTIONS. IF YOU FEEL YOU MUST DEFINE _MSC_VER + * WITH A COMPILER THAT'S NOT MICROSOFT'S C COMPILER, PLEASE CONTACT + * US SO THAT WE CAN MAKE IT SO THAT YOU DON'T HAVE TO DO THAT. THANK + * YOU. + * + * OK, is _MSC_FULL_VER defined? + */ + #if !defined(_MSC_FULL_VER) + /* + * According to + * + * https://sourceforge.net/p/predef/wiki/Compilers/ + * + * with "Visual C++ 6.0 Processor Pack"/Visual C++ 6.0 SP6 and + * later, _MSC_FULL_VER is defined, so either this is an older + * version of Visual C++ or it's not Visual C++ at all. + * + * For Visual C++ 6.0, _MSC_VER is defined as 1200. + */ + #if _MSC_VER > 1200 + /* + * If this is Visual C++, _MSC_FULL_VER should be defined, so we + * assume this isn't Visual C++, and undo the lie that it is. + */ + #undef _MSC_VER + #endif + #endif +#endif + #include #include @@ -127,6 +170,7 @@ typedef struct pcap_addr pcap_addr_t; * of the flags used in the printout phases of tcpdump. * Many fields here are 32 bit ints so compilers won't insert unwanted * padding; these files need to be interchangeable across architectures. + * Documentation: https://www.tcpdump.org/manpages/pcap-savefile.5.txt. * * Do not change the layout of this structure, in any way (this includes * changes that only affect the length of fields in this structure). @@ -154,7 +198,7 @@ typedef struct pcap_addr pcap_addr_t; * * Then supply the changes by forking the branch at * - * https://github.com/the-tcpdump-group/libpcap/issues + * https://github.com/the-tcpdump-group/libpcap/tree/master * * and issuing a pull request, so that future versions of libpcap and * programs that use it (such as tcpdump) will be able to read your new @@ -164,8 +208,8 @@ struct pcap_file_header { bpf_u_int32 magic; u_short version_major; u_short version_minor; - bpf_int32 thiszone; /* gmt to local correction */ - bpf_u_int32 sigfigs; /* accuracy of timestamps */ + bpf_int32 thiszone; /* gmt to local correction; this is always 0 */ + bpf_u_int32 sigfigs; /* accuracy of timestamps; this is always 0 */ bpf_u_int32 snaplen; /* max length saved portion of each pkt */ bpf_u_int32 linktype; /* data link type (LINKTYPE_*) */ }; @@ -201,7 +245,7 @@ typedef enum { struct pcap_pkthdr { struct timeval ts; /* time stamp */ bpf_u_int32 caplen; /* length of portion present */ - bpf_u_int32 len; /* length this packet (off wire) */ + bpf_u_int32 len; /* length of this packet (off wire) */ }; /* @@ -320,36 +364,92 @@ typedef void (*pcap_handler)(u_char *, const struct pcap_pkthdr *, */ #define PCAP_NETMASK_UNKNOWN 0xffffffff +/* + * Initialize pcap. If this isn't called, pcap is initialized to + * a mode source-compatible and binary-compatible with older versions + * that lack this routine. + */ + +/* + * Initialization options. + * All bits not listed here are reserved for expansion. + * + * On UNIX-like systems, the local character encoding is assumed to be + * UTF-8, so no character encoding transformations are done. + * + * On Windows, the local character encoding is the local ANSI code page. + */ +#define PCAP_CHAR_ENC_LOCAL 0x00000000U /* strings are in the local character encoding */ +#define PCAP_CHAR_ENC_UTF_8 0x00000001U /* strings are in UTF-8 */ + +PCAP_AVAILABLE_1_10 +PCAP_API int pcap_init(unsigned int, char *); + /* * We're deprecating pcap_lookupdev() for various reasons (not * thread-safe, can behave weirdly with WinPcap). Callers * should use pcap_findalldevs() and use the first device. */ -PCAP_API char *pcap_lookupdev(char *) -PCAP_DEPRECATED(pcap_lookupdev, "use 'pcap_findalldevs' and use the first device"); +PCAP_AVAILABLE_0_4 +PCAP_DEPRECATED("use 'pcap_findalldevs' and use the first device") +PCAP_API char *pcap_lookupdev(char *); +PCAP_AVAILABLE_0_4 PCAP_API int pcap_lookupnet(const char *, bpf_u_int32 *, bpf_u_int32 *, char *); +PCAP_AVAILABLE_1_0 PCAP_API pcap_t *pcap_create(const char *, char *); + +PCAP_AVAILABLE_1_0 PCAP_API int pcap_set_snaplen(pcap_t *, int); + +PCAP_AVAILABLE_1_0 PCAP_API int pcap_set_promisc(pcap_t *, int); + +PCAP_AVAILABLE_1_0 PCAP_API int pcap_can_set_rfmon(pcap_t *); + +PCAP_AVAILABLE_1_0 PCAP_API int pcap_set_rfmon(pcap_t *, int); + +PCAP_AVAILABLE_1_0 PCAP_API int pcap_set_timeout(pcap_t *, int); + +PCAP_AVAILABLE_1_2 PCAP_API int pcap_set_tstamp_type(pcap_t *, int); + +PCAP_AVAILABLE_1_5 PCAP_API int pcap_set_immediate_mode(pcap_t *, int); + +PCAP_AVAILABLE_1_0 PCAP_API int pcap_set_buffer_size(pcap_t *, int); + +PCAP_AVAILABLE_1_5 PCAP_API int pcap_set_tstamp_precision(pcap_t *, int); + +PCAP_AVAILABLE_1_5 PCAP_API int pcap_get_tstamp_precision(pcap_t *); + +PCAP_AVAILABLE_1_0 PCAP_API int pcap_activate(pcap_t *); +PCAP_AVAILABLE_1_2 PCAP_API int pcap_list_tstamp_types(pcap_t *, int **); + +PCAP_AVAILABLE_1_2 PCAP_API void pcap_free_tstamp_types(int *); + +PCAP_AVAILABLE_1_2 PCAP_API int pcap_tstamp_type_name_to_val(const char *); + +PCAP_AVAILABLE_1_2 PCAP_API const char *pcap_tstamp_type_val_to_name(int); + +PCAP_AVAILABLE_1_2 PCAP_API const char *pcap_tstamp_type_val_to_description(int); #ifdef __linux__ +PCAP_AVAILABLE_1_9 PCAP_API int pcap_set_protocol_linux(pcap_t *, int); #endif @@ -359,8 +459,7 @@ PCAP_API int pcap_set_protocol_linux(pcap_t *, int); * * A system that supports PCAP_TSTAMP_HOST is offering time stamps * provided by the host machine, rather than by the capture device, - * but not committing to any characteristics of the time stamp; - * it will not offer any of the PCAP_TSTAMP_HOST_ subtypes. + * but not committing to any characteristics of the time stamp. * * PCAP_TSTAMP_HOST_LOWPREC is a time stamp, provided by the host machine, * that's low-precision but relatively cheap to fetch; it's normally done @@ -368,10 +467,15 @@ PCAP_API int pcap_set_protocol_linux(pcap_t *, int); * fetch from system calls. * * PCAP_TSTAMP_HOST_HIPREC is a time stamp, provided by the host machine, - * that's high-precision; it might be more expensive to fetch. It might - * or might not be synchronized with the system clock, and might have + * that's high-precision; it might be more expensive to fetch. It is + * synchronized with the system clock. + * + * PCAP_TSTAMP_HOST_HIPREC_UNSYNCED is a time stamp, provided by the host + * machine, that's high-precision; it might be more expensive to fetch. + * It is not synchronized with the system clock, and might have * problems with time stamps for packets received on different CPUs, - * depending on the platform. + * depending on the platform. It might be more likely to be strictly + * monotonic than PCAP_TSTAMP_HOST_HIPREC. * * PCAP_TSTAMP_ADAPTER is a high-precision time stamp supplied by the * capture device; it's synchronized with the system clock. @@ -391,11 +495,12 @@ PCAP_API int pcap_set_protocol_linux(pcap_t *, int); * the packet is received by the network adapter, due to batching * of interrupts for packet arrival, queueing delays, etc.. */ -#define PCAP_TSTAMP_HOST 0 /* host-provided, unknown characteristics */ -#define PCAP_TSTAMP_HOST_LOWPREC 1 /* host-provided, low precision */ -#define PCAP_TSTAMP_HOST_HIPREC 2 /* host-provided, high precision */ -#define PCAP_TSTAMP_ADAPTER 3 /* device-provided, synced with the system clock */ -#define PCAP_TSTAMP_ADAPTER_UNSYNCED 4 /* device-provided, not synced with the system clock */ +#define PCAP_TSTAMP_HOST 0 /* host-provided, unknown characteristics */ +#define PCAP_TSTAMP_HOST_LOWPREC 1 /* host-provided, low precision, synced with the system clock */ +#define PCAP_TSTAMP_HOST_HIPREC 2 /* host-provided, high precision, synced with the system clock */ +#define PCAP_TSTAMP_ADAPTER 3 /* device-provided, synced with the system clock */ +#define PCAP_TSTAMP_ADAPTER_UNSYNCED 4 /* device-provided, not synced with the system clock */ +#define PCAP_TSTAMP_HOST_HIPREC_UNSYNCED 5 /* host-provided, high precision, not synced with the system clock */ /* * Time stamp resolution types. @@ -406,13 +511,25 @@ PCAP_API int pcap_set_protocol_linux(pcap_t *, int); #define PCAP_TSTAMP_PRECISION_MICRO 0 /* use timestamps with microsecond precision, default */ #define PCAP_TSTAMP_PRECISION_NANO 1 /* use timestamps with nanosecond precision */ +PCAP_AVAILABLE_0_4 PCAP_API pcap_t *pcap_open_live(const char *, int, int, int, char *); + +PCAP_AVAILABLE_0_6 PCAP_API pcap_t *pcap_open_dead(int, int); + +PCAP_AVAILABLE_1_5 PCAP_API pcap_t *pcap_open_dead_with_tstamp_precision(int, int, u_int); + +PCAP_AVAILABLE_1_5 PCAP_API pcap_t *pcap_open_offline_with_tstamp_precision(const char *, u_int, char *); + +PCAP_AVAILABLE_0_4 PCAP_API pcap_t *pcap_open_offline(const char *, char *); + #ifdef _WIN32 + PCAP_AVAILABLE_1_5 PCAP_API pcap_t *pcap_hopen_offline_with_tstamp_precision(intptr_t, u_int, char *); + PCAP_API pcap_t *pcap_hopen_offline(intptr_t, char *); /* * If we're building libpcap, these are internal routines in savefile.c, @@ -434,60 +551,154 @@ PCAP_API pcap_t *pcap_open_offline(const char *, char *); pcap_hopen_offline(_get_osfhandle(_fileno(f)), b) #endif #else /*_WIN32*/ + PCAP_AVAILABLE_1_5 PCAP_API pcap_t *pcap_fopen_offline_with_tstamp_precision(FILE *, u_int, char *); + + PCAP_AVAILABLE_0_9 PCAP_API pcap_t *pcap_fopen_offline(FILE *, char *); #endif /*_WIN32*/ +PCAP_AVAILABLE_0_4 PCAP_API void pcap_close(pcap_t *); + +PCAP_AVAILABLE_0_4 PCAP_API int pcap_loop(pcap_t *, int, pcap_handler, u_char *); + +PCAP_AVAILABLE_0_4 PCAP_API int pcap_dispatch(pcap_t *, int, pcap_handler, u_char *); + +PCAP_AVAILABLE_0_4 PCAP_API const u_char *pcap_next(pcap_t *, struct pcap_pkthdr *); + +PCAP_AVAILABLE_0_8 PCAP_API int pcap_next_ex(pcap_t *, struct pcap_pkthdr **, const u_char **); + +PCAP_AVAILABLE_0_8 PCAP_API void pcap_breakloop(pcap_t *); + +PCAP_AVAILABLE_0_4 PCAP_API int pcap_stats(pcap_t *, struct pcap_stat *); + +PCAP_AVAILABLE_0_4 PCAP_API int pcap_setfilter(pcap_t *, struct bpf_program *); + +PCAP_AVAILABLE_0_9 PCAP_API int pcap_setdirection(pcap_t *, pcap_direction_t); + +PCAP_AVAILABLE_0_7 PCAP_API int pcap_getnonblock(pcap_t *, char *); + +PCAP_AVAILABLE_0_7 PCAP_API int pcap_setnonblock(pcap_t *, int, char *); + +PCAP_AVAILABLE_0_9 PCAP_API int pcap_inject(pcap_t *, const void *, size_t); + +PCAP_AVAILABLE_0_8 PCAP_API int pcap_sendpacket(pcap_t *, const u_char *, int); + +PCAP_AVAILABLE_1_0 PCAP_API const char *pcap_statustostr(int); + +PCAP_AVAILABLE_0_4 PCAP_API const char *pcap_strerror(int); + +PCAP_AVAILABLE_0_4 PCAP_API char *pcap_geterr(pcap_t *); + +PCAP_AVAILABLE_0_4 PCAP_API void pcap_perror(pcap_t *, const char *); + +PCAP_AVAILABLE_0_4 PCAP_API int pcap_compile(pcap_t *, struct bpf_program *, const char *, int, bpf_u_int32); + +PCAP_AVAILABLE_0_5 +PCAP_DEPRECATED("use pcap_open_dead(), pcap_compile() and pcap_close()") PCAP_API int pcap_compile_nopcap(int, int, struct bpf_program *, const char *, int, bpf_u_int32); + +/* XXX - this took two arguments in 0.4 and 0.5 */ +PCAP_AVAILABLE_0_6 PCAP_API void pcap_freecode(struct bpf_program *); + +PCAP_AVAILABLE_1_0 PCAP_API int pcap_offline_filter(const struct bpf_program *, const struct pcap_pkthdr *, const u_char *); + +PCAP_AVAILABLE_0_4 PCAP_API int pcap_datalink(pcap_t *); + +PCAP_AVAILABLE_1_0 PCAP_API int pcap_datalink_ext(pcap_t *); + +PCAP_AVAILABLE_0_8 PCAP_API int pcap_list_datalinks(pcap_t *, int **); + +PCAP_AVAILABLE_0_8 PCAP_API int pcap_set_datalink(pcap_t *, int); + +PCAP_AVAILABLE_0_8 PCAP_API void pcap_free_datalinks(int *); + +PCAP_AVAILABLE_0_8 PCAP_API int pcap_datalink_name_to_val(const char *); + +PCAP_AVAILABLE_0_8 PCAP_API const char *pcap_datalink_val_to_name(int); + +PCAP_AVAILABLE_0_8 PCAP_API const char *pcap_datalink_val_to_description(int); + +PCAP_AVAILABLE_1_10 PCAP_API const char *pcap_datalink_val_to_description_or_dlt(int); + +PCAP_AVAILABLE_0_4 PCAP_API int pcap_snapshot(pcap_t *); + +PCAP_AVAILABLE_0_4 PCAP_API int pcap_is_swapped(pcap_t *); + +PCAP_AVAILABLE_0_4 PCAP_API int pcap_major_version(pcap_t *); + +PCAP_AVAILABLE_0_4 PCAP_API int pcap_minor_version(pcap_t *); + +PCAP_AVAILABLE_1_9 PCAP_API int pcap_bufsize(pcap_t *); /* XXX */ +PCAP_AVAILABLE_0_4 PCAP_API FILE *pcap_file(pcap_t *); + +#ifdef _WIN32 +/* + * This probably shouldn't have been kept in WinPcap; most if not all + * UN*X code that used it won't work on Windows. We deprecate it; if + * anybody really needs access to whatever HANDLE may be associated + * with a pcap_t (there's no guarantee that there is one), we can add + * a Windows-only pcap_handle() API that returns the HANDLE. + */ +PCAP_AVAILABLE_0_4 +PCAP_DEPRECATED("request a 'pcap_handle' that returns a HANDLE if you need it") PCAP_API int pcap_fileno(pcap_t *); +#else /* _WIN32 */ +PCAP_AVAILABLE_0_4 +PCAP_API int pcap_fileno(pcap_t *); +#endif /* _WIN32 */ #ifdef _WIN32 PCAP_API int pcap_wsockinit(void); #endif +PCAP_AVAILABLE_0_4 PCAP_API pcap_dumper_t *pcap_dump_open(pcap_t *, const char *); + #ifdef _WIN32 + PCAP_AVAILABLE_0_9 PCAP_API pcap_dumper_t *pcap_dump_hopen(pcap_t *, intptr_t); + /* * If we're building libpcap, this is an internal routine in sf-pcap.c, so * we must not define it as a macro. @@ -506,17 +717,35 @@ PCAP_API pcap_dumper_t *pcap_dump_open(pcap_t *, const char *); pcap_dump_hopen(p, _get_osfhandle(_fileno(f))) #endif #else /*_WIN32*/ + PCAP_AVAILABLE_0_9 PCAP_API pcap_dumper_t *pcap_dump_fopen(pcap_t *, FILE *fp); #endif /*_WIN32*/ + +PCAP_AVAILABLE_1_7 PCAP_API pcap_dumper_t *pcap_dump_open_append(pcap_t *, const char *); + +PCAP_AVAILABLE_0_8 PCAP_API FILE *pcap_dump_file(pcap_dumper_t *); + +PCAP_AVAILABLE_0_9 PCAP_API long pcap_dump_ftell(pcap_dumper_t *); + +PCAP_AVAILABLE_1_9 PCAP_API int64_t pcap_dump_ftell64(pcap_dumper_t *); + +PCAP_AVAILABLE_0_8 PCAP_API int pcap_dump_flush(pcap_dumper_t *); + +PCAP_AVAILABLE_0_4 PCAP_API void pcap_dump_close(pcap_dumper_t *); + +PCAP_AVAILABLE_0_4 PCAP_API void pcap_dump(u_char *, const struct pcap_pkthdr *, const u_char *); +PCAP_AVAILABLE_0_7 PCAP_API int pcap_findalldevs(pcap_if_t **, char *); + +PCAP_AVAILABLE_0_7 PCAP_API void pcap_freealldevs(pcap_if_t *); /* @@ -524,7 +753,7 @@ PCAP_API void pcap_freealldevs(pcap_if_t *); * version string directly. * * On at least some UNIXes, if you import data from a shared library into - * an program, the data is bound into the program binary, so if the string + * a program, the data is bound into the program binary, so if the string * in the version of the library with which the program was linked isn't * the same as the string in the version of the library with which the * program is being run, various undesirable things may happen (warnings, @@ -534,22 +763,9 @@ PCAP_API void pcap_freealldevs(pcap_if_t *); * * On Windows, the string is constructed at run time. */ +PCAP_AVAILABLE_0_8 PCAP_API const char *pcap_lib_version(void); -/* - * On at least some versions of NetBSD and QNX, we don't want to declare - * bpf_filter() here, as it's also be declared in , with a - * different signature, but, on other BSD-flavored UN*Xes, it's not - * declared in , so we *do* want to declare it here, so it's - * declared when we build pcap-bpf.c. - */ -#if !defined(__NetBSD__) && !defined(__QNX__) - PCAP_API u_int bpf_filter(const struct bpf_insn *, const u_char *, u_int, u_int); -#endif -PCAP_API int bpf_validate(const struct bpf_insn *f, int len); -PCAP_API char *bpf_image(const struct bpf_insn *, int); -PCAP_API void bpf_dump(const struct bpf_program *, int); - #if defined(_WIN32) /* @@ -583,7 +799,10 @@ PCAP_API void bpf_dump(const struct bpf_program *, int); PCAP_API HANDLE pcap_getevent(pcap_t *p); + PCAP_AVAILABLE_1_8 PCAP_API int pcap_oid_get_request(pcap_t *, bpf_u_int32, void *, size_t *); + + PCAP_AVAILABLE_1_8 PCAP_API int pcap_oid_set_request(pcap_t *, bpf_u_int32, const void *, size_t *); PCAP_API pcap_send_queue* pcap_sendqueue_alloc(u_int memsize); @@ -626,8 +845,11 @@ PCAP_API void bpf_dump(const struct bpf_program *, int); * UN*X definitions */ + PCAP_AVAILABLE_0_8 PCAP_API int pcap_get_selectable_fd(pcap_t *); - PCAP_API struct timeval *pcap_get_required_select_timeout(pcap_t *); + + PCAP_AVAILABLE_1_9 + PCAP_API const struct timeval *pcap_get_required_select_timeout(pcap_t *); #endif /* _WIN32/MSDOS/UN*X */ @@ -657,17 +879,20 @@ PCAP_API void bpf_dump(const struct bpf_program *, int); /* * The formats allowed by pcap_open() are the following: * - file://path_and_filename [opens a local file] - * - rpcap://devicename [opens the selected device devices available on the local host, without using the RPCAP protocol] + * - rpcap://devicename [opens the selected device available on the local host, without using the RPCAP protocol] * - rpcap://host/devicename [opens the selected device available on a remote host] * - rpcap://host:port/devicename [opens the selected device available on a remote host, using a non-standard port for RPCAP] - * - adaptername [to open a local adapter; kept for compability, but it is strongly discouraged] - * - (NULL) [to open the first local adapter; kept for compability, but it is strongly discouraged] + * - adaptername [to open a local adapter; kept for compatibility, but it is strongly discouraged] + * - (NULL) [to open the first local adapter; kept for compatibility, but it is strongly discouraged] * * The formats allowed by the pcap_findalldevs_ex() are the following: * - file://folder/ [lists all the files in the given folder] * - rpcap:// [lists all local adapters] * - rpcap://host:port/ [lists the devices available on a remote host] * + * In all the above, "rpcaps://" can be substituted for "rpcap://" to enable + * SSL (if it has been compiled in). + * * Referring to the 'host' and 'port' parameters, they can be either numeric or literal. Since * IPv6 is fully supported, these are the allowed formats: * @@ -728,7 +953,7 @@ PCAP_API void bpf_dump(const struct bpf_program *, int); #define PCAP_OPENFLAG_DATATX_UDP 0x00000002 /* - * Specifies wheether the remote probe will capture its own generated + * Specifies whether the remote probe will capture its own generated * traffic. * * In case the remote probe uses the same interface to capture traffic @@ -789,15 +1014,16 @@ PCAP_API void bpf_dump(const struct bpf_program *, int); * authentication is successful (and the user has the right to open network * devices) the RPCAP connection will continue; otherwise it will be dropped. * - * *******NOTE********: the username and password are sent over the network - * to the capture server *IN CLEAR TEXT*. Don't use this on a network - * that you don't completely control! (And be *really* careful in your - * definition of "completely"!) + * *******NOTE********: unless TLS is being used, the username and password + * are sent over the network to the capture server *IN CLEAR TEXT*. Don't + * use this, without TLS (i.e., with rpcap:// rather than rpcaps://) on + * a network that you don't completely control! (And be *really* careful + * in your definition of "completely"!) */ #define RPCAP_RMTAUTH_PWD 1 /* - * This structure keeps the information needed to autheticate the user + * This structure keeps the information needed to authenticate the user * on a remote machine. * * The remote machine can either grant or refuse the access according @@ -856,10 +1082,15 @@ struct pcap_rmtauth * For opening a remote capture, pcap_open() is currently the only * API available. */ +PCAP_AVAILABLE_1_9 PCAP_API pcap_t *pcap_open(const char *source, int snaplen, int flags, int read_timeout, struct pcap_rmtauth *auth, char *errbuf); + +PCAP_AVAILABLE_1_9 PCAP_API int pcap_createsrcstr(char *source, int type, const char *host, const char *port, const char *name, char *errbuf); + +PCAP_AVAILABLE_1_9 PCAP_API int pcap_parsesrcstr(const char *source, int *type, char *host, char *port, char *name, char *errbuf); @@ -882,6 +1113,7 @@ PCAP_API int pcap_parsesrcstr(const char *source, int *type, char *host, * For listing remote capture devices, pcap_findalldevs_ex() is currently * the only API available. */ +PCAP_AVAILABLE_1_9 PCAP_API int pcap_findalldevs_ex(const char *source, struct pcap_rmtauth *auth, pcap_if_t **alldevs, char *errbuf); @@ -952,6 +1184,7 @@ struct pcap_samp /* * New functions. */ +PCAP_AVAILABLE_1_9 PCAP_API struct pcap_samp *pcap_setsampling(pcap_t *p); /* @@ -961,12 +1194,24 @@ PCAP_API struct pcap_samp *pcap_setsampling(pcap_t *p); /* Maximum length of an host name (needed for the RPCAP active mode) */ #define RPCAP_HOSTLIST_SIZE 1024 +PCAP_AVAILABLE_1_9 PCAP_API SOCKET pcap_remoteact_accept(const char *address, const char *port, const char *hostlist, char *connectinghost, struct pcap_rmtauth *auth, char *errbuf); + +PCAP_AVAILABLE_1_10 +PCAP_API SOCKET pcap_remoteact_accept_ex(const char *address, const char *port, + const char *hostlist, char *connectinghost, + struct pcap_rmtauth *auth, int uses_ssl, char *errbuf); + +PCAP_AVAILABLE_1_9 PCAP_API int pcap_remoteact_list(char *hostlist, char sep, int size, char *errbuf); + +PCAP_AVAILABLE_1_9 PCAP_API int pcap_remoteact_close(const char *host, char *errbuf); + +PCAP_AVAILABLE_1_9 PCAP_API void pcap_remoteact_cleanup(void); #ifdef __cplusplus diff --git a/pcap/socket.h b/pcap/socket.h index 6f27cba12f91..ee2e393e1b69 100644 --- a/pcap/socket.h +++ b/pcap/socket.h @@ -48,15 +48,6 @@ #include #include - /* - * Winsock doesn't have this UN*X type; it's used in the UN*X - * sockets API. - * - * XXX - do we need to worry about UN*Xes so old that *they* - * don't have it, either? - */ - typedef int socklen_t; - /* * Winsock doesn't have this POSIX type; it's used for the * tv_usec value of struct timeval. diff --git a/pcap_activate.3pcap b/pcap_activate.3pcap index 162a929331e4..169a7565f1cf 100644 --- a/pcap_activate.3pcap +++ b/pcap_activate.3pcap @@ -17,7 +17,7 @@ .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" -.TH PCAP_ACTIVATE 3PCAP "31 July 2016" +.TH PCAP_ACTIVATE 3PCAP "24 November 2019" .SH NAME pcap_activate \- activate a capture handle .SH SYNOPSIS @@ -31,13 +31,15 @@ int pcap_activate(pcap_t *p); .ft .fi .SH DESCRIPTION -.B pcap_activate() +.BR pcap_activate () is used to activate a packet capture handle to look at packets on the network, with the options that were set on the handle being in effect. .SH RETURN VALUE -.B pcap_activate() -returns 0 on success without warnings, a non-zero positive value on +.BR pcap_activate () +returns +.B 0 +on success without warnings, a non-zero positive value on success with warnings, and a negative value on error. A non-zero return value indicates what warning or error condition occurred. @@ -50,15 +52,15 @@ promiscuous mode. .TP .B PCAP_WARNING_TSTAMP_TYPE_NOTSUP The time stamp type specified in a previous -.B pcap_set_tstamp_type(3PCAP) +.BR pcap_set_tstamp_type (3PCAP) call isn't supported by the capture source (the time stamp type is left as the default), .TP .B PCAP_WARNING Another warning condition occurred; -.B pcap_geterr(3PCAP) +.BR pcap_geterr (3PCAP) or -.B pcap_perror(3PCAP) +.BR pcap_perror (3PCAP) may be called with .I p as an argument to fetch or display a message describing the warning @@ -89,9 +91,9 @@ The capture source device is not up. .TP .B PCAP_ERROR Another error occurred. -.B pcap_geterr() +.BR pcap_geterr () or -.B pcap_perror() +.BR pcap_perror () may be called with .I p as an argument to fetch or display a message describing the error. @@ -102,9 +104,9 @@ If or .B PCAP_ERROR_PERM_DENIED is returned, -.B pcap_geterr() +.BR pcap_geterr () or -.B pcap_perror() +.BR pcap_perror () may be called with .I p as an argument to fetch or display an message giving additional details @@ -115,8 +117,15 @@ Additional warning and error codes may be added in the future; a program should check for positive, negative, and zero return codes, and treat all positive return codes as warnings and all negative return codes as errors. -.B pcap_statustostr(3PCAP) +.BR pcap_statustostr (3PCAP) can be called, with a warning or error code as an argument, to fetch a message describing the warning or error code. +.LP +If +.BR pcap_activate () +fails, the +.I pcap_t * +is not closed and freed; it should be closed using +.BR pcap_close (). .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap_breakloop.3pcap b/pcap_breakloop.3pcap index cc000d2e075c..3cd74db275da 100644 --- a/pcap_breakloop.3pcap +++ b/pcap_breakloop.3pcap @@ -17,7 +17,7 @@ .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" -.TH PCAP_BREAKLOOP 3PCAP "25 July 2018" +.TH PCAP_BREAKLOOP 3PCAP "8 December 2022" .SH NAME pcap_breakloop \- force a pcap_dispatch() or pcap_loop() call to return .SH SYNOPSIS @@ -31,19 +31,31 @@ void pcap_breakloop(pcap_t *); .ft .fi .SH DESCRIPTION -.B pcap_breakloop() +.BR pcap_breakloop () sets a flag that will force -.B pcap_dispatch(3PCAP) +.BR pcap_dispatch (3PCAP) or -.B pcap_loop(3PCAP) +.BR pcap_loop (3PCAP) to return rather than looping; they will return the number of packets that have been processed so far, or .B PCAP_ERROR_BREAK -if no packets have been processed so far. +if no packets have been processed so far. If the loop is currently +blocked waiting for packets to arrive, +.BR pcap_breakloop () +will also, on some platforms, wake up the thread that is blocked. In +this version of libpcap, the only platforms on which a wakeup is caused +by +.BR pcap_breakloop () +are Linux and Windows, and the wakeup will only be caused when capturing +on network interfaces; it will not be caused on other operating systems, +and will not be caused on any OS when capturing on other types of +devices. .PP This routine is safe to use inside a signal handler on UNIX or a console -control handler on Windows, as it merely sets a flag that is checked -within the loop. +control handler on Windows, or in a thread other than the one in which +the loop is running, as it merely sets a flag that is checked within the +loop and, on some platforms, performs a signal-safe and thread-safe API +call. .PP The flag is checked in loops reading packets from the OS - a signal by itself will not necessarily terminate those loops - as well as in loops @@ -61,55 +73,76 @@ packets arrive and the call completes. .PP .ft B Note also that, in a multi-threaded application, if one thread is -blocked in pcap_dispatch(), pcap_loop(), pcap_next(3PCAP), or pcap_next_ex(3PCAP), -a call to pcap_breakloop() in a different thread will not unblock that -thread. +blocked in pcap_dispatch(), pcap_loop(), pcap_next(3PCAP), or +pcap_next_ex(3PCAP), a call to pcap_breakloop() in a different thread +will only unblock that thread on the platforms and capture devices +listed above. +.PP +If a non-zero packet buffer timeout is set on the +.BR pcap_t , +and you are capturing on a network interface, the thread will be +unblocked with the timeout expires. This is not guaranteed to happen +unless at least one packet has arrived; the only platforms on which it +happens are macOS, the BSDs, Solaris 11, AIX, Tru64 UNIX, and Windows. +.PP +If you want to ensure that the loop will eventually be unblocked on any +other platforms, or unblocked when capturing on a device other than a +network interface, you will need to use whatever mechanism the OS +provides for breaking a thread out of blocking calls in order to unblock +the thread, such as thread cancellation or thread signalling in systems +that support POSIX threads. .ft R -You will need to use whatever mechanism the OS provides for -breaking a thread out of blocking calls in order to unblock the thread, -such as thread cancellation or thread signalling in systems that support -POSIX threads, or -.B SetEvent() -on the result of -.B pcap_getevent() -on a -.B pcap_t -on which the thread is blocked on Windows. Asynchronous procedure calls -will not work on Windows, as a thread blocked on a -.B pcap_t -will not be in an alertable state. +.PP +.ft B +Note that if pcap_breakloop() unblocks the thread capturing packets, and +you are running on a platform that supports packet buffering, there may +be packets in the buffer that arrived before pcap_breakloop() were +called but that weren't yet provided to libpcap, those packets will not +have been processed by pcap_dispatch() or pcap_loop(). If +pcap_breakloop() was called in order to terminate the capture process, +then, in order to process those packets, you would have to call +pcap_dispatch() one time in order to process the last batch of packets. +This may block until the packet buffer timeout expires, so a non-zero +packet buffer timeout must be used. .ft R .PP Note that -.B pcap_next() +.BR pcap_next () and -.B pcap_next_ex() +.BR pcap_next_ex () will, on some platforms, loop reading packets from the OS; that loop will not necessarily be terminated by a signal, so -.B pcap_breakloop() +.BR pcap_breakloop () should be used to terminate packet processing even if -.B pcap_next() +.BR pcap_next () or -.B pcap_next_ex() +.BR pcap_next_ex () is being used. .PP -.B pcap_breakloop() +.BR pcap_breakloop () does not guarantee that no further packets will be processed by -.B pcap_dispatch() +.BR pcap_dispatch () or -.B pcap_loop() +.BR pcap_loop () after it is called; at most one more packet might be processed. .PP If .B PCAP_ERROR_BREAK is returned from -.B pcap_dispatch() +.BR pcap_dispatch () or -.BR pcap_loop() , +.BR pcap_loop (), the flag is cleared, so a subsequent call will resume reading packets. If a positive number is returned, the flag is not cleared, so a subsequent call will return .B PCAP_ERROR_BREAK and clear the flag. +.SH BACKWARD COMPATIBILITY +.PP +This function became available in libpcap release 0.8.1. +.PP +In releases prior to libpcap 1.10.0, +.BR pcap_breakloop () +will not wake up a blocked thread on any platform. .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap_can_set_rfmon.3pcap b/pcap_can_set_rfmon.3pcap index 0baac7a646c7..be03956bb955 100644 --- a/pcap_can_set_rfmon.3pcap +++ b/pcap_can_set_rfmon.3pcap @@ -31,13 +31,16 @@ int pcap_can_set_rfmon(pcap_t *p); .ft .fi .SH DESCRIPTION -.B pcap_can_set_rfmon() +.BR pcap_can_set_rfmon () checks whether monitor mode could be set on a capture handle when the handle is activated. .SH RETURN VALUE -.B pcap_can_set_rfmon() -returns 0 if monitor mode could not be set, -1 if monitor mode could be set, and a negative value on error. +.BR pcap_can_set_rfmon () +returns +.B 0 +if monitor mode could not be set, +.B 1 +if monitor mode could be set, and a negative value on error. A negative return value indicates what error condition occurred. The possible error values are: .TP @@ -54,19 +57,24 @@ The capture handle has already been activated. .TP .B PCAP_ERROR Another error occurred. -.B pcap_geterr(3PCAP) +.BR pcap_geterr (3PCAP) or -.B \%pcap_perror(3PCAP) +.BR \%pcap_perror (3PCAP) may be called with .I p as an argument to fetch or display a message describing the error. .LP Additional error codes may be added in the future; a program should -check for 0, 1, and negative, return codes, and treat all negative +check for +.BR 0 , +.BR 1 , +and negative, return codes, and treat all negative return codes as errors. -.B pcap_statustostr(3PCAP) +.BR pcap_statustostr (3PCAP) can be called, with a warning or error code as an argument, to fetch a message describing the warning or error code. .SH SEE ALSO -pcap(3PCAP), pcap_create(3PCAP), pcap_activate(3PCAP), -pcap_set_rfmon(3PCAP) +.BR pcap (3PCAP), +.BR pcap_create (3PCAP), +.BR pcap_activate (3PCAP), +.BR pcap_set_rfmon (3PCAP) diff --git a/pcap_close.3pcap b/pcap_close.3pcap index e2316a417dfd..c4ac3fad8eec 100644 --- a/pcap_close.3pcap +++ b/pcap_close.3pcap @@ -31,9 +31,9 @@ void pcap_close(pcap_t *p); .ft .fi .SH DESCRIPTION -.B pcap_close() +.BR pcap_close () closes the files associated with .I p and deallocates resources. .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap_compile.3pcap.in b/pcap_compile.3pcap.in index 824f52b32e7e..67001fa49322 100644 --- a/pcap_compile.3pcap.in +++ b/pcap_compile.3pcap.in @@ -33,17 +33,17 @@ const char *str, int optimize, bpf_u_int32 netmask); .ft .fi .SH DESCRIPTION -.B pcap_compile() +.BR pcap_compile () is used to compile the string .I str into a filter program. See -.BR pcap-filter (@MAN_MISC_INFO@) +.BR \%pcap-filter (@MAN_MISC_INFO@) for the syntax of that string. -.I program +.I fp is a pointer to a .I bpf_program struct and is filled in by -.BR pcap_compile() . +.BR pcap_compile (). .I optimize controls whether optimization on the resulting code is performed. .I netmask @@ -59,24 +59,26 @@ for IPv4 broadcast addresses will fail to compile, but all other tests in the filter program will be OK. .LP NOTE: in libpcap 1.8.0 and later, -.B pcap_compile() +.BR pcap_compile () can be used in multiple threads within a single process. However, in earlier versions of libpcap, it is .I not safe to use -.B pcap_compile() +.BR pcap_compile () in multiple threads in a single process without some form of mutual exclusion allowing only one thread to call it at any given time. .SH RETURN VALUE -.B pcap_compile() -returns 0 on success and +.BR pcap_compile () +returns +.B 0 +on success and .B PCAP_ERROR on failure. If .B PCAP_ERROR is returned, -.B pcap_geterr(3PCAP) +.BR pcap_geterr (3PCAP) or -.B pcap_perror(3PCAP) +.BR pcap_perror (3PCAP) may be called with .I p as an argument to fetch or display the error text. @@ -86,4 +88,6 @@ The .B PCAP_NETMASK_UNKNOWN constant became available in libpcap release 1.1.0. .SH SEE ALSO -pcap(3PCAP), pcap_setfilter(3PCAP), pcap_freecode(3PCAP), +.BR pcap (3PCAP), +.BR pcap_setfilter (3PCAP), +.BR pcap_freecode (3PCAP) diff --git a/pcap_create.3pcap b/pcap_create.3pcap index 5a15007bfc30..bd42d733f6cb 100644 --- a/pcap_create.3pcap +++ b/pcap_create.3pcap @@ -36,7 +36,7 @@ pcap_t *pcap_create(const char *source, char *errbuf); .ft .fi .SH DESCRIPTION -.B pcap_create() +.BR pcap_create () is used to create a packet capture handle to look at packets on the network. .I source @@ -48,12 +48,12 @@ argument of "any" or can be used to capture packets from all interfaces. .PP The returned handle must be activated with -.B pcap_activate(3PCAP) +.BR pcap_activate (3PCAP) before packets can be captured with it; options for the capture, such as promiscuous mode, can be set on the handle before activating it. .SH RETURN VALUE -.B pcap_create() +.BR pcap_create () returns a .I pcap_t * on success and @@ -69,4 +69,4 @@ is assumed to be able to hold at least .B PCAP_ERRBUF_SIZE chars. .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap_datalink.3pcap.in b/pcap_datalink.3pcap.in index 26203683eddf..abb737508420 100644 --- a/pcap_datalink.3pcap.in +++ b/pcap_datalink.3pcap.in @@ -31,19 +31,19 @@ int pcap_datalink(pcap_t *p); .ft .fi .SH DESCRIPTION -.B pcap_datalink() +.BR pcap_datalink () returns the link-layer header type for the live capture or ``savefile'' specified by .IR p . .PP It must not be called on a pcap descriptor created by -.B \%pcap_create(3PCAP) +.BR \%pcap_create (3PCAP) that has not yet been activated by -.BR \%pcap_activate(3PCAP) . +.BR \%pcap_activate (3PCAP). .PP .I https://www.tcpdump.org/linktypes.html lists the values -.B pcap_datalink() +.BR pcap_datalink () can return and describes the packet formats that correspond to those values. .PP @@ -55,14 +55,17 @@ any given link-layer header type, such as for Ethernet. For example, the "any" device on Linux will have a link-layer header type of .B DLT_LINUX_SLL +or +.B DLT_LINUX_SLL2 even if all devices on the system at the time the "any" device is opened have some other data link type, such as .B DLT_EN10MB for Ethernet. .SH RETURN VALUE -.B pcap_datalink() +.BR pcap_datalink () returns the link-layer header type on success and .B PCAP_ERROR_NOT_ACTIVATED if called on a capture handle that has been created but not activated. .SH SEE ALSO -pcap(3PCAP), pcap-linktype(@MAN_MISC_INFO@) +.BR pcap (3PCAP), +.BR pcap-linktype (@MAN_MISC_INFO@) diff --git a/pcap_datalink_name_to_val.3pcap b/pcap_datalink_name_to_val.3pcap index dd4688f0a260..d2cb103106e4 100644 --- a/pcap_datalink_name_to_val.3pcap +++ b/pcap_datalink_name_to_val.3pcap @@ -32,7 +32,7 @@ int pcap_datalink_name_to_val(const char *name); .ft .fi .SH DESCRIPTION -.B pcap_datalink_name_to_val() +.BR pcap_datalink_name_to_val () translates a link-layer header type name, which is a .B DLT_ name with the @@ -40,10 +40,10 @@ name with the removed, to the corresponding link-layer header type value. The translation is case-insensitive. .SH RETURN VALUE -.B pcap_datalink_name_to_val() +.BR pcap_datalink_name_to_val () returns the type value on success and .B PCAP_ERROR if the name is not a known -type name.. +type name. .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap_datalink_val_to_name.3pcap b/pcap_datalink_val_to_name.3pcap index f42165fbaca3..41790950af31 100644 --- a/pcap_datalink_val_to_name.3pcap +++ b/pcap_datalink_val_to_name.3pcap @@ -17,7 +17,7 @@ .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" -.TH PCAP_DATALINK_VAL_TO_NAME 3PCAP "12 October 2016" +.TH PCAP_DATALINK_VAL_TO_NAME 3PCAP "4 May 2022" .SH NAME pcap_datalink_val_to_name, pcap_datalink_val_to_description, pcap_datalink_val_to_description_or_dlt \- get a @@ -35,7 +35,7 @@ const char *pcap_datalink_val_to_description_or_dlt(int dlt); .ft .fi .SH DESCRIPTION -.B pcap_datalink_val_to_name() +.BR pcap_datalink_val_to_name () translates a link-layer header type value to the corresponding link-layer header type name, which is the .B DLT_ @@ -47,7 +47,7 @@ is returned if the type value does not correspond to a known .B DLT_ value. .PP -.B pcap_datalink_val_to_description() +.BR pcap_datalink_val_to_description () translates a link-layer header type value to a short description of that link-layer header type. .B NULL @@ -55,12 +55,22 @@ is returned if the type value does not correspond to a known .B DLT_ value. .PP -.B pcap_datalink_val_to_description_or_dlt() +.BR pcap_datalink_val_to_description_or_dlt () translates a link-layer header type value to a short description of that -link-layer header type just like pcap_datalink_val_to_description. +link-layer header type just like +.BR pcap_datalink_val_to_description (). If the type value does not correspond to a known .B DLT_ value, the string "DLT n" is returned, where n is the value of the dlt argument. +.SH BACKWARD COMPATIBILITY +The +.BR pcap_datalink_val_to_description_or_dlt () +function first became available in libpcap release 1.9.1. In previous +releases, +.BR pcap_datalink_val_to_description () +would have to be called and, if it returned +.BR NULL , +a default string would have to be constructed. .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap_dump.3pcap b/pcap_dump.3pcap index 7f201b7ce7d1..ba7f75adb392 100644 --- a/pcap_dump.3pcap +++ b/pcap_dump.3pcap @@ -33,18 +33,18 @@ u_char *sp); .ft .fi .SH DESCRIPTION -.B pcap_dump() +.BR pcap_dump () outputs a packet to the ``savefile'' opened with -.BR pcap_dump_open(3PCAP) . +.BR pcap_dump_open (3PCAP). Note that its calling arguments are suitable for use with -.B pcap_dispatch(3PCAP) +.BR pcap_dispatch (3PCAP) or -.BR pcap_loop(3PCAP) . +.BR pcap_loop (3PCAP). If called directly, the .I user parameter is of type .B pcap_dumper_t as returned by -.BR pcap_dump_open() . +.BR pcap_dump_open (). .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap_dump_close.3pcap b/pcap_dump_close.3pcap index bd95a52b290e..a62eb34dbf6c 100644 --- a/pcap_dump_close.3pcap +++ b/pcap_dump_close.3pcap @@ -31,7 +31,9 @@ void pcap_dump_close(pcap_dumper_t *p); .ft .fi .SH DESCRIPTION -.B pcap_dump_close() +.BR pcap_dump_close () closes the ``savefile.'' .SH SEE ALSO -pcap(3PCAP), pcap_dump_open(3PCAP), pcap_dump(3PCAP) +.BR pcap (3PCAP), +.BR pcap_dump_open (3PCAP), +.BR pcap_dump (3PCAP) diff --git a/pcap_dump_file.3pcap b/pcap_dump_file.3pcap index d2074312d1ec..9fd6c7ecc38b 100644 --- a/pcap_dump_file.3pcap +++ b/pcap_dump_file.3pcap @@ -31,8 +31,8 @@ FILE *pcap_dump_file(pcap_dumper_t *p); .ft .fi .SH DESCRIPTION -.B pcap_dump_file() +.BR pcap_dump_file () returns the standard I/O stream of the ``savefile'' opened by -.BR pcap_dump_open(3PCAP) . +.BR pcap_dump_open (3PCAP). .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap_dump_flush.3pcap b/pcap_dump_flush.3pcap index 5d1747452d3e..c6e0f86ed70c 100644 --- a/pcap_dump_flush.3pcap +++ b/pcap_dump_flush.3pcap @@ -31,15 +31,18 @@ int pcap_dump_flush(pcap_dumper_t *p); .ft .fi .SH DESCRIPTION -.B pcap_dump_flush() +.BR pcap_dump_flush () flushes the output buffer to the ``savefile,'' so that any packets written with -.B pcap_dump(3PCAP) +.BR pcap_dump (3PCAP) but not yet written to the ``savefile'' will be written. .SH RETURN VALUE -.B pcap_dump_flush() -returns 0 on success and +.BR pcap_dump_flush () +returns +.B 0 +on success and .B PCAP_ERROR on failure. .SH SEE ALSO -pcap(3PCAP), pcap_dump_open(3PCAP) +.BR pcap (3PCAP), +.BR pcap_dump_open (3PCAP) diff --git a/pcap_dump_ftell.3pcap b/pcap_dump_ftell.3pcap index 20cb995eab35..062d60957a9f 100644 --- a/pcap_dump_ftell.3pcap +++ b/pcap_dump_ftell.3pcap @@ -33,18 +33,18 @@ int64_t pcap_dump_ftell64(pcap_dumper_t *p); .ft .fi .SH DESCRIPTION -.B pcap_dump_ftell() +.BR pcap_dump_ftell () returns the current file position for the ``savefile'', representing the number of bytes written by -.B pcap_dump_open(3PCAP) +.BR pcap_dump_open (3PCAP) and -.BR pcap_dump(3PCAP) . +.BR pcap_dump (3PCAP). .B PCAP_ERROR is returned on error. If the current file position does not fit in a .BR long , it will be truncated; this can happen on 32-bit UNIX-like systems with large file support and on Windows. -.B pcap_dump_ftell64() +.BR pcap_dump_ftell64 () returns the current file position in a .BR int64_t , so if file offsets that don't fit in a @@ -54,5 +54,11 @@ but that fit in a are supported, this will return the file offset without truncation. .B PCAP_ERROR is returned on error. +.SH BACKWARD COMPATIBILITY +The function +.BR pcap_dump_ftell64 () +became available in libpcap release 1.9.0. In previous releases, there +was no mechanism to obtain a file offset that is too large to fit in a +.BR long . .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap_dump_open.3pcap.in b/pcap_dump_open.3pcap.in index b86696f08826..555484ffcdbc 100644 --- a/pcap_dump_open.3pcap.in +++ b/pcap_dump_open.3pcap.in @@ -17,9 +17,10 @@ .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" -.TH PCAP_DUMP_OPEN 3PCAP "22 August 2018" +.TH PCAP_DUMP_OPEN 3PCAP "3 July 2020" .SH NAME -pcap_dump_open, pcap_dump_fopen \- open a file to which to write packets +pcap_dump_open, pcap_dump_open_append, pcap_dump_fopen \- open a file to +which to write packets .SH SYNOPSIS .nf .ft B @@ -34,7 +35,7 @@ pcap_dumper_t *pcap_dump_fopen(pcap_t *p, FILE *fp); .ft .fi .SH DESCRIPTION -.B pcap_dump_open() +.BR pcap_dump_open () is called to open a ``savefile'' for writing. .I fname specifies the name of the file to open. The file will have @@ -42,62 +43,66 @@ the same format as those used by .BR tcpdump (1) and .BR tcpslice (1). +If the file does not exist, it will be created; if the file exists, it +will be truncated and overwritten. The name "-" is a synonym for .BR stdout . .PP -.B pcap_dump_fopen() +.BR pcap_dump_fopen () is called to write data to an existing open stream .IR fp ; this stream will be closed by a subsequent call to -.BR pcap_dump_close(3PCAP) . +.BR pcap_dump_close (3PCAP). +The stream is assumed to be at the beginning of a file that has been +newly created or truncated, so that writes will start at the beginning +of the file. Note that on Windows, that stream should be opened in binary mode. .PP .I p is a capture or ``savefile'' handle returned by an earlier call to -.B pcap_create(3PCAP) +.BR pcap_create (3PCAP) and activated by an earlier call to -.BR \%pcap_activate(3PCAP) , +.BR \%pcap_activate (3PCAP), or returned by an earlier call to -.BR \%pcap_open_offline(3PCAP) , -.BR pcap_open_live(3PCAP) , +.BR \%pcap_open_offline (3PCAP), +.BR pcap_open_live (3PCAP), or -.BR pcap_open_dead(3PCAP) . +.BR pcap_open_dead (3PCAP). The time stamp precision, link-layer type, and snapshot length from .I p are used as the link-layer type and snapshot length of the output file. .PP -.B pcap_dump_open_append() +.BR pcap_dump_open_append () is like -.B pcap_dump_open() -but does not create the file if it does not exist and, if it does -already exist, and is a pcap file with the same byte order as the host -opening the file, and has the same time stamp precision, link-layer -header type, and snapshot length as +.BR pcap_dump_open () +but, if the file already exists, and is a pcap file with the same byte +order as the host opening the file, and has the same time stamp +precision, link-layer header type, and snapshot length as .IR p , it will write new packets at the end of the file. -.SH RETURN VALUES +.SH RETURN VALUE A pointer to a .B pcap_dumper_t structure to use in subsequent -.B pcap_dump(3PCAP) +.BR pcap_dump (3PCAP) and -.B pcap_dump_close(3PCAP) +.BR pcap_dump_close (3PCAP) calls is returned on success. .B NULL is returned on failure. If .B NULL is returned, -.B pcap_geterr(3PCAP) +.BR pcap_geterr (3PCAP) can be used to get the error text. .SH BACKWARD COMPATIBILITY .PP The -.B pcap_dump_open_append() +.BR pcap_dump_open_append () function became available in libpcap release 1.7.2. In previous releases, there is no support for appending packets to an existing savefile. .SH SEE ALSO -pcap(3PCAP), -\%pcap-savefile(@MAN_FILE_FORMATS@) +.BR pcap (3PCAP), +.BR \%pcap-savefile (@MAN_FILE_FORMATS@) diff --git a/pcap_file.3pcap b/pcap_file.3pcap index 981451ba5684..74029732d333 100644 --- a/pcap_file.3pcap +++ b/pcap_file.3pcap @@ -31,29 +31,29 @@ FILE *pcap_file(pcap_t *p); .ft .fi .SH DESCRIPTION -.B pcap_file() +.BR pcap_file () returns the standard I/O stream of the ``savefile,'' if a ``savefile'' was opened with -.BR pcap_open_offline(3PCAP) , +.BR pcap_open_offline (3PCAP), or .BR NULL , if a network device was opened with -.B pcap_create(3PCAP) +.BR pcap_create (3PCAP) and -.BR \%pcap_activate(3PCAP) , +.BR \%pcap_activate (3PCAP), or with -.BR pcap_open_live(3PCAP) . +.BR pcap_open_live (3PCAP). .PP Note that the Packet Capture library is usually built with large file support, so the standard I/O stream of the ``savefile'' might refer to a file larger than 2 gigabytes; applications that use -.B pcap_file() +.BR pcap_file () should, if possible, use calls that support large files on the return value of -.B pcap_file() +.BR pcap_file () or the value returned by -.B fileno(3) +.BR fileno (3) when passed the return value of -.BR pcap_file() . +.BR pcap_file (). .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap_fileno.3pcap b/pcap_fileno.3pcap index 60ac12945dd1..6d0edf88dcf8 100644 --- a/pcap_fileno.3pcap +++ b/pcap_fileno.3pcap @@ -35,31 +35,31 @@ If .I p refers to a network device that was opened for a live capture using a combination of -.B pcap_create(3PCAP) +.BR pcap_create (3PCAP) and -.BR pcap_activate(3PCAP) , +.BR pcap_activate (3PCAP), or using -.BR pcap_open_live(3PCAP) , -.B pcap_fileno() +.BR pcap_open_live (3PCAP), +.BR pcap_fileno () returns the file descriptor from which captured packets are read. .LP If .I p refers to a ``savefile'' that was opened using functions such as -.BR pcap_open_offline(3PCAP) +.BR pcap_open_offline (3PCAP) or -.BR pcap_fopen_offline(3PCAP) , +.BR pcap_fopen_offline (3PCAP), a ``dead'' .B pcap_t opened using -.BR pcap_open_dead(3PCAP) , +.BR pcap_open_dead (3PCAP), or a .B pcap_t that was created with -.B pcap_create() +.BR pcap_create () but that has not yet been activated with -.BR pcap_activate() , +.BR pcap_activate (), it returns .BR PCAP_ERROR . .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap_findalldevs.3pcap b/pcap_findalldevs.3pcap index 712e255a0fdf..0dcc0af69d18 100644 --- a/pcap_findalldevs.3pcap +++ b/pcap_findalldevs.3pcap @@ -17,7 +17,7 @@ .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" -.TH PCAP_FINDALLDEVS 3PCAP "22 August 2018" +.TH PCAP_FINDALLDEVS 3PCAP "23 August 2018" .SH NAME pcap_findalldevs, pcap_freealldevs \- get a list of capture devices, and free that list @@ -38,21 +38,21 @@ void pcap_freealldevs(pcap_if_t *alldevs); .ft .fi .SH DESCRIPTION -.B pcap_findalldevs() +.BR pcap_findalldevs () constructs a list of network devices that can be opened with -.B pcap_create(3PCAP) +.BR pcap_create (3PCAP) and -.B pcap_activate(3PCAP) +.BR pcap_activate (3PCAP) or with -.BR pcap_open_live(3PCAP) . +.BR pcap_open_live (3PCAP). (Note that there may be network devices that cannot be opened by the process calling -.BR pcap_findalldevs() , +.BR pcap_findalldevs (), because, for example, that process does not have sufficient privileges to open them for capturing; if so, those devices will not appear on the list.) If -.B pcap_findalldevs() +.BR pcap_findalldevs () succeeds, the pointer pointed to by .I alldevsp is set to point to the first element of the list, or to @@ -72,7 +72,7 @@ for the last element of the list .TP .B name a pointer to a string giving a name for the device to pass to -.B pcap_open_live() +.BR pcap_open_live () .TP .B description if not @@ -194,15 +194,19 @@ for IPv6 addresses, it can be interpreted as if it pointed to a .BR "struct sockaddr_in6". .PP The list of devices must be freed with -.BR pcap_freealldevs(3PCAP) , +.BR pcap_freealldevs (3PCAP), which frees the list pointed to by .IR alldevs . .SH RETURN VALUE -.B pcap_findalldevs() -returns 0 on success and +.BR pcap_findalldevs () +returns +.B 0 +on success and .B PCAP_ERROR on failure; as indicated, finding no -devices is considered success, rather than failure, so 0 will be +devices is considered success, rather than failure, so +.B 0 +will be returned in that case. If .B PCAP_ERROR is returned, @@ -228,4 +232,4 @@ and .B PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE constants became available in libpcap release 1.9.0. .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap_freecode.3pcap b/pcap_freecode.3pcap index 4e71efa49cf2..65915fa1c7eb 100644 --- a/pcap_freecode.3pcap +++ b/pcap_freecode.3pcap @@ -31,13 +31,13 @@ void pcap_freecode(struct bpf_program *); .ft .fi .SH DESCRIPTION -.B pcap_freecode() +.BR pcap_freecode () is used to free up allocated memory pointed to by a .I bpf_program struct generated by -.B pcap_compile(3PCAP) +.BR pcap_compile (3PCAP) when that BPF program is no longer needed, for example after it has been made the filter program for a pcap structure by a call to -.BR pcap_setfilter(3PCAP) . +.BR pcap_setfilter (3PCAP). .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap_get_required_select_timeout.3pcap b/pcap_get_required_select_timeout.3pcap index e58cb4e78d5c..37af180352ed 100644 --- a/pcap_get_required_select_timeout.3pcap +++ b/pcap_get_required_select_timeout.3pcap @@ -17,10 +17,10 @@ .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" -.TH PCAP_GET_REQUIRED_SELECT_TIMEOUT 3PCAP "25 July 2018" +.TH PCAP_GET_REQUIRED_SELECT_TIMEOUT 3PCAP "29 January 2020" .SH NAME -pcap_get_required_select_timeout \- get a file descriptor on which a -select() can be done for a live capture +pcap_get_required_select_timeout \- get a timeout to be used when doing +select() for a live capture .SH SYNOPSIS .nf .ft B @@ -28,53 +28,98 @@ select() can be done for a live capture .ft .LP .ft B -struct timeval *pcap_get_required_select_timeout(pcap_t *p); +const struct timeval *pcap_get_required_select_timeout(pcap_t *p); .ft .fi .SH DESCRIPTION -.B pcap_get_required_select_timeout() +.BR pcap_get_required_select_timeout () returns, on UNIX, a pointer to a .B struct timeval containing a value that must be used as the minimum timeout in -.BR select(2) , -.BR poll(2) , -.BR epoll_wait(2) , +.BR select (2), +.BR poll (2), +.BR epoll_wait (2), and -.B kevent() -calls if -.B pcap_get_selectable_fd(3PCAP) +.BR kevent (2) +calls, or +.B NULL +if there is no such timeout. +If a +.RB non- NULL +value is returned, it must be used regardless of whether +.BR pcap_get_selectable_fd (3PCAP) returns -.BR PCAP_ERROR . -.PP -The timeout that should be used in those calls must be no larger than -the smallest of all timeouts returned by -.B \%pcap_get_required_select_timeout() -for devices from which packets will be captured. -.PP -The device for which -.B pcap_get_selectable_fd() -returned -.B PCAP_ERROR -must be put in non-blocking mode with -.BR pcap_setnonblock(3PCAP) , -and an attempt must always be made to read packets from the device -when the -.BR select() , -.BR poll() , -.BR epoll_wait() , +.B \-1 +for any descriptor on which those calls are being done. +.BR pcap_get_required_select_timeout () +should be called for all +.BR pcap_t s +before a call to +.BR select (), +.BR poll (), +.BR epoll_wait (), or -.B kevent() -call returns. +.BR kevent (), +and any timeouts used for those calls should be updated as appropriate +given the new value of the timeout. +.PP +For +.BR kevent (), +one +.B EVFILT_TIMER +filter per selectable descriptor can be used, rather than using the +timeout argument to +.BR kevent (); +if the +.B EVFILT_TIMER +event for a particular selectable descriptor signals an event, +.BR pcap_dispatch (3PCAP) +should be called for the corresponding +.BR pcap_t . +.PP +On Linux systems with +.BR timerfd_create (2), +one timer object created by +.BR timerfd_create () +per selectable descriptor can be used, rather than using the timeout +argument to +.BR epoll_wait (); +if the +timer object for a particular selectable descriptor signals an event, +.BR pcap_dispatch (3PCAP) +should be called for the corresponding +.BR pcap_t . +.PP +Otherwise, a timeout value no larger than +the smallest of all timeouts returned by +.BR \%pcap_get_required_select_timeout () +for devices from which packets will be captured and any other timeouts +to be used in the call should be used as the timeout for the call, and, +when the call returns, +.BR pcap_dispatch (3PCAP) +should be called for all +.BR pcap_t s +for which a +.RB non- NULL +timeout was returned, regardless of whether it's indicated as having +anything to read from it or not. +.PP +All devices with a +.RB non- NULL +timeout must be put in non-blocking mode with +.BR pcap_setnonblock (3PCAP). .PP Note that a device on which a read can be done without blocking may, on some platforms, not have any packets to read if the packet buffer timeout has expired. A call to -.B pcap_dispatch(3PCAP) +.BR pcap_dispatch () or -.B pcap_next_ex(3PCAP) -will return 0 in this case, but will not block. +.BR pcap_next_ex (3PCAP) +will return +.B 0 +in this case, but will not block. .PP -.B pcap_get_required_select_timeout() +.BR pcap_get_required_select_timeout () is not available on Windows. .SH RETURN VALUE A pointer to a @@ -85,14 +130,41 @@ is returned. .SH BACKWARD COMPATIBILITY This function became available in libpcap release 1.9.0. In previous releases, -.BR select() , -.BR poll() , -.BR epoll_wait() , +.BR select (), +.BR poll (), +.BR epoll_wait (), and -.B kevent() -cannot be used on any capture source for which -.B pcap_get_selectable_fd -returns \-1. +.BR kevent () +could not be used for devices that don't provide a selectable file +descriptor (in other words, on any capture source for that +.BR pcap_get_selectable_fd () +returns +.BR \-1 ). +.PP +In libpcap release 1.10.0 and later, the timeout value can change from +call to call, so +.BR pcap_get_required_select_timeout () +must be called before each call to +.BR select (), +.BR poll (), +.BR epoll_wait (), +or +.BR kevent (), +and the new value must be used to calculate timeouts for the call. Code +that does that will also work with libpcap 1.9.x releases, so code +using +.BR pcap_get_required_select_timeout () +should be changed to call it for each call to +.BR select (), +.BR poll (), +.BR epoll_wait (), +or +.BR kevent () +even if the code must also work with libpcap 1.9.x. .SH SEE ALSO -pcap(3PCAP), pcap_get_selectable_fd(3PCAP), select(2), poll(2), -epoll_wait(2), kqueue(2) +.BR pcap (3PCAP), +.BR pcap_get_selectable_fd (3PCAP), +.BR select (2), +.BR poll (2), +.BR epoll_wait (2), +.BR kqueue (2) diff --git a/pcap_get_selectable_fd.3pcap b/pcap_get_selectable_fd.3pcap index 7f43db398e61..ed333038a0a1 100644 --- a/pcap_get_selectable_fd.3pcap +++ b/pcap_get_selectable_fd.3pcap @@ -17,7 +17,7 @@ .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" -.TH PCAP_GET_SELECTABLE_FD 3PCAP "25 July 2018" +.TH PCAP_GET_SELECTABLE_FD 3PCAP "29 January 2020" .SH NAME pcap_get_selectable_fd \- get a file descriptor on which a select() can be done for a live capture @@ -32,42 +32,42 @@ int pcap_get_selectable_fd(pcap_t *p); .ft .fi .SH DESCRIPTION -.B pcap_get_selectable_fd() +.BR pcap_get_selectable_fd () returns, on UNIX, a file descriptor number for a file descriptor on which one can do a -.BR select(2) , -.BR poll(2) , -.BR epoll_wait(2) , -.BR kevent() , +.BR select (2), +.BR poll (2), +.BR epoll_wait (2), +.BR kevent (2), or other such call to wait for it to be possible to read packets without blocking, if such a descriptor exists, or -.BR PCAP_ERROR , +.BR \-1 , if no such descriptor exists. .PP Some network devices opened with -.B pcap_create(3PCAP) +.BR pcap_create (3PCAP) and -.BR pcap_activate(3PCAP) , +.BR pcap_activate (3PCAP), or with -.BR pcap_open_live(3PCAP) , +.BR pcap_open_live (3PCAP), do not support those calls (for example, regular network devices on FreeBSD 4.3 and 4.4, and Endace DAG devices), so -.B PCAP_ERROR +.B \-1 is returned for those devices. In that case, those calls must be given a timeout less than or equal to the timeout returned by -.B pcap_get_required_select_timeout(3PCAP) +.BR pcap_get_required_select_timeout (3PCAP) for the device for which -.B pcap_get_selectable_fd() +.BR pcap_get_selectable_fd () returned -.BR PCAP_ERROR , +.BR \-1 , the device must be put in non-blocking mode with a call to -.BR \%pcap_setnonblock(3PCAP) , +.BR \%pcap_setnonblock (3PCAP), and an attempt must always be made to read packets from the device when the call returns. If -.B \%pcap_get_required_select_timeout() +.BR \%pcap_get_required_select_timeout () returns .BR NULL , it is not possible to wait for packets to arrive on the device in an @@ -76,9 +76,9 @@ event loop. Note that a device on which a read can be done without blocking may, on some platforms, not have any packets to read if the packet buffer timeout has expired. A call to -.B pcap_dispatch(3PCAP) +.BR pcap_dispatch (3PCAP) or -.B pcap_next_ex(3PCAP) +.BR pcap_next_ex (3PCAP) will return 0 in this case, but will not block. .PP Note that in: @@ -91,18 +91,18 @@ OpenBSD prior to OpenBSD 2.4; .IP Mac OS X prior to Mac OS X 10.7; .PP -.BR select() , -.BR poll() , +.BR select (), +.BR poll (), and -.B kevent() +.BR kevent () do not work correctly on BPF devices; -.B pcap_get_selectable_fd() +.BR pcap_get_selectable_fd () will return a file descriptor on most of those versions (the exceptions being FreeBSD 4.3 and 4.4), but a simple -.BR select() , -.BR poll() , +.BR select (), +.BR poll (), or -.B kevent() +.BR kevent () call will not indicate that the descriptor is readable until a full buffer's worth of packets is received, even if the packet timeout expires before then. To work around this, code that uses @@ -119,33 +119,34 @@ work correctly on BPF devices, so the workaround isn't necessary, although it does no harm.) .PP Note also that -.B poll() +.BR poll () and -.B kevent() +.BR kevent () doesn't work on character special files, including BPF devices, in Mac OS X 10.4 and 10.5, so, while -.B select() +.BR select () can be used on the descriptor returned by -.BR pcap_get_selectable_fd() , -.B poll() +.BR pcap_get_selectable_fd (), +.BR poll () and -.B kevent() +.BR kevent () cannot be used on it those versions of Mac OS X. -.BR poll() , +.BR poll (), but not -.BR kevent() , +.BR kevent (), works on that descriptor in Mac OS X releases prior to 10.4; -.B poll() +.BR poll () and -.B kevent() +.BR kevent () work on that descriptor in Mac OS X 10.6 and later. .PP -.B pcap_get_selectable_fd() +.BR pcap_get_selectable_fd () is not available on Windows. .SH RETURN VALUE A selectable file descriptor is returned if one exists; otherwise, -.B PCAP_ERROR +.B \-1 is returned. .SH SEE ALSO -pcap(3PCAP), kqueue(2) +.BR pcap (3PCAP), +.BR kqueue (2) diff --git a/pcap_get_tstamp_precision.3pcap.in b/pcap_get_tstamp_precision.3pcap.in index 2e72e0bac14f..46451446ba55 100644 --- a/pcap_get_tstamp_precision.3pcap.in +++ b/pcap_get_tstamp_precision.3pcap.in @@ -19,7 +19,7 @@ .\"IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED .\"WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -.TH PCAP_GET_TSTAMP_PRECISION 3PCAP "18 December 2013" +.TH PCAP_GET_TSTAMP_PRECISION 3PCAP "23 August 2018" .SH NAME pcap_get_tstamp_precision \- get the time stamp precision returned in captures @@ -34,11 +34,11 @@ int pcap_get_tstamp_precision(pcap_t *p); .ft .fi .SH DESCRIPTION -.B pcap_get_tstamp_precision() +.BR pcap_get_tstamp_precision () returns the precision of the time stamp returned in packet captures on the pcap descriptor. .SH RETURN VALUE -.B pcap_get_tstamp_precision() +.BR pcap_get_tstamp_precision () returns .B PCAP_TSTAMP_PRECISION_MICRO or @@ -51,6 +51,6 @@ This function became available in libpcap release 1.5.1. In previous releases, time stamps from a capture device or savefile are always given in seconds and microseconds. .SH SEE ALSO -pcap(3PCAP), -pcap_set_tstamp_precision(3PCAP), -pcap-tstamp(@MAN_MISC_INFO@) +.BR pcap (3PCAP), +.BR pcap_set_tstamp_precision (3PCAP), +.BR \%pcap-tstamp (@MAN_MISC_INFO@) diff --git a/pcap_geterr.3pcap b/pcap_geterr.3pcap index ee681c8c2296..72e23e38e06c 100644 --- a/pcap_geterr.3pcap +++ b/pcap_geterr.3pcap @@ -32,7 +32,7 @@ void pcap_perror(pcap_t *p, const char *prefix); .ft .fi .SH DESCRIPTION -.B pcap_geterr() +.BR pcap_geterr () returns the error text pertaining to the last pcap library error. .BR NOTE : the pointer it returns will no longer point to a valid error message @@ -42,10 +42,10 @@ passed to it is closed; you must use or copy the string before closing the .BR pcap_t . .PP -.B pcap_perror() +.BR pcap_perror () prints the text of the last pcap library error on .BR stderr , prefixed by .IR prefix . .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap_init.3pcap b/pcap_init.3pcap new file mode 100644 index 000000000000..a807d0ecd86c --- /dev/null +++ b/pcap_init.3pcap @@ -0,0 +1,99 @@ +.\" Copyright (c) 1994, 1996, 1997 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that: (1) source code distributions +.\" retain the above copyright notice and this paragraph in its entirety, (2) +.\" distributions including binary code include the above copyright notice and +.\" this paragraph in its entirety in the documentation or other materials +.\" provided with the distribution, and (3) all advertising materials mentioning +.\" features or use of this software display the following acknowledgement: +.\" ``This product includes software developed by the University of California, +.\" Lawrence Berkeley Laboratory and its contributors.'' Neither the name of +.\" the University nor the names of its contributors may be used to endorse +.\" or promote products derived from this software without specific prior +.\" written permission. +.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +.\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +.\" +.TH PCAP_INIT 3PCAP "4 May 2022" +.SH NAME +pcap_init \- initialize the library +.SH SYNOPSIS +.nf +.ft B +#include +.ft +.LP +.nf +.ft B +char errbuf[PCAP_ERRBUF_SIZE]; +.ft +.LP +.ft B +int pcap_init(unsigned int opts, char *errbuf); +.ft +.fi +.SH DESCRIPTION +.BR pcap_init () +is used to initialize the Packet Capture library. +.I opts +specifies options for the library; +currently, the options are: +.TP +.B PCAP_CHAR_ENC_LOCAL +Treat all strings supplied as arguments, and return all strings to the +caller, as being in the local character encoding. +.TP +.B PCAP_CHAR_ENC_UTF_8 +Treat all strings supplied as arguments, and return all strings to the +caller, as being in UTF-8. +.PP +On UNIX-like systems, the local character encoding is assumed to be +UTF-8, so no character encoding transformations are done. +.PP +On Windows, the local character encoding is the local ANSI code page. +.PP +If +.BR pcap_init () +is not called, strings are treated as being in the local ANSI code page +on Windows, +.BR pcap_lookupdev (3PCAP) +will succeed if there is a device on which to capture, and +.BR pcap_create (3PCAP) +makes an attempt to check whether the string passed as an argument is a +UTF-16LE string - note that this attempt is unsafe, as it may run past +the end of the string - to handle +.BR pcap_lookupdev () +returning a UTF-16LE string. Programs that don't call +.BR pcap_init () +should, on Windows, call +.BR pcap_wsockinit () +to initialize Winsock; this is not necessary if +.BR pcap_init () +is called, as +.BR pcap_init () +will initialize Winsock itself on Windows. +.SH RETURN VALUE +.BR pcap_init () +returns +.B 0 +on success and +.B PCAP_ERROR +on failure. +If +.B PCAP_ERROR +is returned, +.I errbuf +is filled in with an appropriate error message. +.I errbuf +is assumed to be able to hold at least +.B PCAP_ERRBUF_SIZE +chars. +.SH BACKWARD COMPATIBILITY +This function became available in libpcap release 1.9.0. In previous +releases, on Windows, all strings supplied as arguments, and all strings +returned to the caller, are in the local character encoding. +.SH SEE ALSO +.BR pcap (3PCAP) diff --git a/pcap_inject.3pcap b/pcap_inject.3pcap index 92a92638849b..83eae1c3846d 100644 --- a/pcap_inject.3pcap +++ b/pcap_inject.3pcap @@ -17,7 +17,7 @@ .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" -.TH PCAP_INJECT 3PCAP "25 July 2018" +.TH PCAP_INJECT 3PCAP "5 March 2022" .SH NAME pcap_inject, pcap_sendpacket \- transmit a packet .SH SYNOPSIS @@ -32,7 +32,7 @@ int pcap_sendpacket(pcap_t *p, const u_char *buf, int size); .ft .fi .SH DESCRIPTION -.B pcap_inject() +.BR pcap_inject () sends a raw packet through the network interface; .I buf points to the data of the packet, including the link-layer header, and @@ -42,7 +42,7 @@ is the number of bytes in the packet. Note that, even if you successfully open the network interface, you might not have permission to send packets on it, or it might not support sending packets; as -.B pcap_open_live(3PCAP) +.BR pcap_open_live (3PCAP) doesn't have a flag to indicate whether to open for capturing, sending, or capturing and sending, you cannot request an open that supports sending and be notified at open time whether sending will be possible. @@ -51,7 +51,7 @@ Note also that some devices might not support sending packets. Note that, on some platforms, the link-layer header of the packet that's sent might not be the same as the link-layer header of the packet supplied to -.BR pcap_inject() , +.BR pcap_inject (), as the source link-layer address, if the header contains such an address, might be changed to be the address assigned to the interface on which the packet it sent, if the platform doesn't support sending @@ -61,34 +61,42 @@ libpcap used when attaching to the device, even on platforms that .I do nominally support sending completely raw and unchanged packets. .PP -.B pcap_sendpacket() +.BR pcap_sendpacket () is like -.BR pcap_inject() , -but it returns 0 on success, rather than returning the number of bytes +.BR pcap_inject (), +but it returns +.B 0 +on success, rather than returning the number of bytes written. -.RB ( pcap_inject() +.RB ( pcap_inject () comes from OpenBSD; -.B pcap_sendpacket() -comes from WinPcap. Both are provided for compatibility.) +.BR pcap_sendpacket () +comes from WinPcap/Npcap. Both are provided for compatibility.) .SH RETURN VALUE -.B pcap_inject() -returns the number of bytes written on success and +.BR pcap_inject () +returns the number of bytes written on success, +.B PCAP_ERROR_NOT_ACTIVATED +if called on a capture handle that has been created but not activated, and .B PCAP_ERROR -on failure. +on other errors. .PP -.B pcap_sendpacket() -returns 0 on success and +.BR pcap_sendpacket () +returns +.B 0 +on success, +.B PCAP_ERROR_NOT_ACTIVATED +if called on a capture handle that has been created but not activated, and .B PCAP_ERROR -on failure. +on other errors. .PP If .B PCAP_ERROR is returned, -.B pcap_geterr(3PCAP) +.BR pcap_geterr (3PCAP) or -.B pcap_perror(3PCAP) +.BR pcap_perror (3PCAP) may be called with .I p as an argument to fetch or display the error text. .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap_is_swapped.3pcap b/pcap_is_swapped.3pcap index 67f762fedcfa..260601a97029 100644 --- a/pcap_is_swapped.3pcap +++ b/pcap_is_swapped.3pcap @@ -31,21 +31,21 @@ int pcap_is_swapped(pcap_t *p); .ft .fi .SH DESCRIPTION -.B pcap_is_swapped() -returns true (1) if +.BR pcap_is_swapped () +returns true (\fB1\fP) if .I p refers to a ``savefile'' that uses a different byte order than the current system. For a live capture, it always returns false -(0). +(\fB0\fP). .PP It must not be called on a pcap descriptor created by -.B \%pcap_create(3PCAP) +.BR \%pcap_create (3PCAP) that has not yet been activated by -.BR \%pcap_activate(3PCAP) . +.BR \%pcap_activate (3PCAP). .SH RETURN VALUE -.B pcap_is_swapped() -returns true (1) or false (0) on success and +.BR pcap_is_swapped () +returns true (\fB1\fP) or false (\fB0\fP) on success and .B PCAP_ERROR_NOT_ACTIVATED if called on a capture handle that has been created but not activated. .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap_lib_version.3pcap b/pcap_lib_version.3pcap index 4b86b2d5771c..73ed2b8864ba 100644 --- a/pcap_lib_version.3pcap +++ b/pcap_lib_version.3pcap @@ -31,9 +31,9 @@ const char *pcap_lib_version(void); .ft .fi .SH DESCRIPTION -.B pcap_lib_version() +.BR pcap_lib_version () returns a pointer to a string giving information about the version of the libpcap library being used; note that it contains more information than just a version number. .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap_list_datalinks.3pcap.in b/pcap_list_datalinks.3pcap.in index 60ba478f7466..d6e314997aa1 100644 --- a/pcap_list_datalinks.3pcap.in +++ b/pcap_list_datalinks.3pcap.in @@ -33,25 +33,25 @@ void pcap_free_datalinks(int *dlt_list); .ft .fi .SH DESCRIPTION -.B pcap_list_datalinks() +.BR pcap_list_datalinks () is used to get a list of the supported link-layer header types of the interface associated with the pcap descriptor. -.B pcap_list_datalinks() +.BR pcap_list_datalinks () allocates an array to hold the list and sets .IR *dlt_buf to point to that array. .LP The caller is responsible for freeing the array with -.BR pcap_free_datalinks() , +.BR pcap_free_datalinks (), which frees the list of link-layer header types pointed to by .IR dlt_list . .LP It must not be called on a pcap descriptor created by -.B \%pcap_create(3PCAP) +.BR \%pcap_create (3PCAP) that has not yet been activated by -.BR \%pcap_activate(3PCAP) . +.BR \%pcap_activate (3PCAP). .SH RETURN VALUE -.B pcap_list_datalinks() +.BR pcap_list_datalinks () returns the number of link-layer header types in the array on success, .B PCAP_ERROR_NOT_ACTIVATED if called on a capture handle that has been created but not activated, @@ -61,13 +61,13 @@ on other errors. If .B PCAP_ERROR is returned, -.B pcap_geterr(3PCAP) +.BR pcap_geterr (3PCAP) or -.B \%pcap_perror(3PCAP) +.BR \%pcap_perror (3PCAP) may be called with .I p as an argument to fetch or display the error text. .SH SEE ALSO -pcap(3PCAP), -pcap_datalink_val_to_name(3PCAP), -pcap-linktype(@MAN_MISC_INFO@) +.BR pcap (3PCAP), +.BR pcap_datalink_val_to_name (3PCAP), +.BR pcap-linktype (@MAN_MISC_INFO@) diff --git a/pcap_list_tstamp_types.3pcap.in b/pcap_list_tstamp_types.3pcap.in index e2487f704fd8..8b6174f4c498 100644 --- a/pcap_list_tstamp_types.3pcap.in +++ b/pcap_list_tstamp_types.3pcap.in @@ -18,7 +18,7 @@ .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" -.TH PCAP_LIST_TSTAMP_TYPES 3PCAP "22 August 2018" +.TH PCAP_LIST_TSTAMP_TYPES 3PCAP "8 September 2019" .SH NAME pcap_list_tstamp_types, pcap_free_tstamp_types \- get a list of time stamp types supported by a capture device, and free that list @@ -34,23 +34,23 @@ void pcap_free_tstamp_types(int *tstamp_types); .ft .fi .SH DESCRIPTION -.B pcap_list_tstamp_types() +.BR pcap_list_tstamp_types () is used to get a list of the supported time stamp types of the interface associated with the pcap descriptor. -.B pcap_list_tstamp_types() +.BR pcap_list_tstamp_types () allocates an array to hold the list and sets .I *tstamp_typesp to point to the array. See -.BR pcap-tstamp (@MAN_MISC_INFO@) +.BR \%pcap-tstamp (@MAN_MISC_INFO@) for a list of all the time stamp types. .PP The caller is responsible for freeing the array with -.BR pcap_free_tstamp_types() , +.BR pcap_free_tstamp_types (), which frees the list pointed to by .IR tstamp_types . .SH RETURN VALUE -.B pcap_list_tstamp_types() +.BR pcap_list_tstamp_types () returns the number of time stamp types in the array on success and .B PCAP_ERROR on failure. @@ -65,9 +65,9 @@ one or more types). If .B PCAP_ERROR is returned, -.B pcap_geterr(3PCAP) +.BR pcap_geterr (3PCAP) or -.B pcap_perror(3PCAP) +.BR pcap_perror (3PCAP) may be called with .I p as an argument to fetch or display the error text. @@ -77,5 +77,6 @@ These functions became available in libpcap release 1.2.1. In previous releases, the time stamp type cannot be set; only the default time stamp type offered by a capture source is available. .SH SEE ALSO -pcap(3PCAP), pcap_tstamp_type_val_to_name(3PCAP), -pcap-tstamp(@MAN_MISC_INFO@) +.BR pcap (3PCAP), +.BR pcap_tstamp_type_val_to_name (3PCAP), +.BR \%pcap-tstamp (@MAN_MISC_INFO@) diff --git a/pcap_lookupdev.3pcap b/pcap_lookupdev.3pcap index 29f09e375297..b5d548f009d3 100644 --- a/pcap_lookupdev.3pcap +++ b/pcap_lookupdev.3pcap @@ -39,22 +39,30 @@ char errbuf[PCAP_ERRBUF_SIZE]; .B This interface is obsoleted by .BR pcap_findalldevs (3PCAP). To find a default device on which to capture, call -.B pcap_findalldevs() +.BR pcap_findalldevs () and, if the list it returns is not empty, use the first device in the list. (If the list is empty, there are no devices on which capture is possible.) .LP -.B pcap_lookupdev() +.B If +.BR pcap_init (3PCAP) +.B has been called, this interface always returns +.BR NULL . +.LP +.BR pcap_lookupdev () returns a pointer to a string giving the name of a network device suitable for use with -.B pcap_create(3PCAP) +.BR pcap_create (3PCAP) and -.BR \%pcap_activate(3PCAP) , +.BR \%pcap_activate (3PCAP), or with -.BR pcap_open_live(3PCAP) , +.BR pcap_open_live (3PCAP), and with -.BR pcap_lookupnet(3PCAP) . +.BR pcap_lookupnet (3PCAP). If there is an error, +or if +.BR pcap_init (3PCAP) +has been called, .B NULL is returned and .I errbuf @@ -64,16 +72,15 @@ is assumed to be able to hold at least .B PCAP_ERRBUF_SIZE chars. .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) .SH BUGS The pointer returned by -.B pcap_lookupdev() +.BR pcap_lookupdev () points to a static buffer; subsequent calls to -.B pcap_lookupdev() +.BR pcap_lookupdev () in the same thread, or calls to -.B pcap_lookupdev() +.BR pcap_lookupdev () in another thread, may overwrite that buffer. .LP -In WinPcap, this function may return a UTF-16 string rather than an -ASCII or UTF-8 string. - +In WinPcap and Npcap, this function may return a UTF-16 string rather +than an ASCII or UTF-8 string. diff --git a/pcap_lookupnet.3pcap b/pcap_lookupnet.3pcap index f609445311c4..c12fa55ed5ac 100644 --- a/pcap_lookupnet.3pcap +++ b/pcap_lookupnet.3pcap @@ -38,7 +38,7 @@ bpf_u_int32 *maskp, char *errbuf); .ft .fi .SH DESCRIPTION -.B pcap_lookupnet() +.BR pcap_lookupnet () is used to determine the IPv4 network number and mask associated with the network device .IR device . @@ -50,8 +50,10 @@ are .I bpf_u_int32 pointers. .SH RETURN VALUE -.B pcap_lookupnet() -returns 0 on success and +.BR pcap_lookupnet () +returns +.B 0 +on success and .B PCAP_ERROR on failure. If .B PCAP_ERROR @@ -63,4 +65,4 @@ is assumed to be able to hold at least .B PCAP_ERRBUF_SIZE chars. .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap_loop.3pcap b/pcap_loop.3pcap index 0193714b885b..0c5952627d10 100644 --- a/pcap_loop.3pcap +++ b/pcap_loop.3pcap @@ -17,7 +17,7 @@ .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" -.TH PCAP_LOOP 3PCAP "25 July 2018" +.TH PCAP_LOOP 3PCAP "5 March 2022" .SH NAME pcap_loop, pcap_dispatch \- process packets from a live capture or savefile .SH SYNOPSIS @@ -42,28 +42,32 @@ pcap_handler callback, u_char *user); .ft .fi .SH DESCRIPTION -.B pcap_loop() +.BR pcap_loop () processes packets from a live capture or ``savefile'' until .I cnt packets are processed, the end of the ``savefile'' is reached when reading from a ``savefile'', -.B pcap_breakloop(3PCAP) +.BR pcap_breakloop (3PCAP) is called, or an error occurs. It does .B not return when live packet buffer timeouts occur. -A value of \-1 or 0 for +A value of +.B \-1 +or +.B 0 +for .I cnt is equivalent to infinity, so that packets are processed until another ending condition occurs. .PP -.B pcap_dispatch() +.BR pcap_dispatch () processes packets from a live capture or ``savefile'' until .I cnt packets are processed, the end of the current bufferful of packets is reached when doing a live capture, the end of the ``savefile'' is reached when reading from a ``savefile'', -.B pcap_breakloop() +.BR pcap_breakloop () is called, or an error occurs. Thus, when doing a live capture, .I cnt @@ -71,7 +75,11 @@ is the maximum number of packets to process before returning, but is not a minimum number; when reading a live capture, only one bufferful of packets is read at a time, so fewer than .I cnt -packets may be processed. A value of \-1 or 0 for +packets may be processed. A value of +.B \-1 +or +.B 0 +for .I cnt causes all the packets received in one buffer to be processed when reading a live capture, and causes all the packets in the file to be @@ -79,20 +87,11 @@ processed when reading a ``savefile''. .PP Note that, when doing a live capture on some platforms, if the read timeout expires when there are no packets available, -.B pcap_dispatch() +.BR pcap_dispatch () will return 0, even when not in non-blocking mode, as there are no packets to process. Applications should be prepared for this to happen, but must not rely on it happening. .PP -.ft B -(In older versions of libpcap, the behavior when -\fIcnt\fP -was 0 was undefined; different platforms and devices behaved -differently, so code that must work with older versions of libpcap -should use \-1, not 0, as the value of -\fIcnt\fP.) -.ft R -.PP .I callback specifies a .I pcap_handler @@ -102,9 +101,9 @@ a pointer which is passed in the .I user argument to -.B pcap_loop() +.BR pcap_loop () or -.BR pcap_dispatch() , +.BR pcap_dispatch (), a .I const struct pcap_pkthdr pointer pointing to the packet time stamp and lengths, and a @@ -123,25 +122,25 @@ them. .PP The bytes of data from the packet begin with a link-layer header. The format of the link-layer header is indicated by the return value of the -.B pcap_datalink(3PCAP) +.BR pcap_datalink (3PCAP) routine when handed the .B pcap_t value also passed to -.B pcap_loop() +.BR pcap_loop () or -.BR pcap_dispatch() . +.BR pcap_dispatch (). .I https://www.tcpdump.org/linktypes.html lists the values -.B pcap_datalink() +.BR pcap_datalink () can return and describes the packet formats that correspond to those values. The value it returns will be valid for all packets received unless and until -.B pcap_set_datalink(3PCAP) +.BR pcap_set_datalink (3PCAP) is called; after a successful call to -.BR pcap_set_datalink() , +.BR pcap_set_datalink (), all subsequent packets will have a link-layer header of the type specified by the link-layer header type value passed to -.BR pcap_set_datalink() . +.BR pcap_set_datalink (). .PP Do .B NOT @@ -151,28 +150,35 @@ any given link-layer header type, such as for Ethernet. For example, the "any" device on Linux will have a link-layer header type of .B DLT_LINUX_SLL +or +.B DLT_LINUX_SLL2 even if all devices on the system at the time the "any" device is opened have some other data link type, such as .B DLT_EN10MB for Ethernet. .SH RETURN VALUE -.B pcap_loop() -returns 0 if +.BR pcap_loop () +returns +.B 0 +if .I cnt is exhausted or if, when reading from a ``savefile'', no more packets are available. It returns -.B PCAP_ERROR -if an error occurs or .B PCAP_ERROR_BREAK if the loop terminated due to a call to -.B pcap_breakloop() -before any packets were processed. +.BR pcap_breakloop () +before any packets were processed, +.B PCAP_ERROR_NOT_ACTIVATED +if called on a capture handle that has been created but not activated, +or +.B PCAP_ERROR +if another error occurs. It does .B not return when live packet buffer timeouts occur; instead, it attempts to read more packets. .PP -.B pcap_dispatch() +.BR pcap_dispatch () returns the number of packets processed on success; this can be 0 if no packets were read from a live capture (if, for example, they were discarded because they didn't pass the packet filter, or if, on @@ -181,12 +187,15 @@ packets arrive, the timeout expires before any packets arrive, or if the file descriptor for the capture device is in non-blocking mode and no packets were available to be read) or if no more packets are available in a ``savefile.'' It returns -.B PCAP_ERROR -if an error occurs or .B PCAP_ERROR_BREAK if the loop terminated due to a call to -.B pcap_breakloop() -before any packets were processed. +.BR pcap_breakloop () +before any packets were processed, +.B PCAP_ERROR_NOT_ACTIVATED +if called on a capture handle that has been created but not activated, +or +.B PCAP_ERROR +if another error occurs. .ft B If your application uses pcap_breakloop(), make sure that you explicitly check for PCAP_ERROR and PCAP_ERROR_BREAK, @@ -196,11 +205,24 @@ rather than just checking for a return value < 0. If .B PCAP_ERROR is returned, -.B pcap_geterr(3PCAP) +.BR pcap_geterr (3PCAP) or -.B pcap_perror(3PCAP) +.BR pcap_perror (3PCAP) may be called with .I p as an argument to fetch or display the error text. +.SH BACKWARD COMPATIBILITY +.PP +In libpcap versions before 1.5.0, the behavior when +.I cnt +was +.B 0 +was undefined; different platforms and devices behaved differently, +so code that must work with these versions of libpcap should use +.BR \-1 , +not +.BR 0 , +as the value of +.IR cnt . .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap_major_version.3pcap b/pcap_major_version.3pcap index 2fedfd2d7847..393705b4be01 100644 --- a/pcap_major_version.3pcap +++ b/pcap_major_version.3pcap @@ -35,9 +35,9 @@ int pcap_minor_version(pcap_t *p); If .I p refers to a ``savefile'', -.B pcap_major_version() +.BR pcap_major_version () returns the major number of the file format of the ``savefile'' and -.B pcap_minor_version() +.BR pcap_minor_version () returns the minor number of the file format of the ``savefile''. The version number is stored in the ``savefile''; note that the meaning of its values depends on the type of ``savefile'' (for example, pcap or @@ -46,9 +46,9 @@ pcapng). If .I p refers to a live capture, the values returned by -.B pcap_major_version() +.BR pcap_major_version () and -.B pcap_minor_version() +.BR pcap_minor_version () are not meaningful. .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap_next_ex.3pcap b/pcap_next_ex.3pcap index f0eb82d1ea0f..4d2c43e87368 100644 --- a/pcap_next_ex.3pcap +++ b/pcap_next_ex.3pcap @@ -17,7 +17,7 @@ .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" -.TH PCAP_NEXT_EX 3PCAP "25 July 2018" +.TH PCAP_NEXT_EX 3PCAP "5 March 2022" .SH NAME pcap_next_ex, pcap_next \- read the next packet from a pcap_t .SH SYNOPSIS @@ -34,7 +34,7 @@ const u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h); .ft .fi .SH DESCRIPTION -.B pcap_next_ex() +.BR pcap_next_ex () reads the next packet and returns a success/failure indication. If the packet was read without problems, the pointer pointed to by the .I pkt_header @@ -47,16 +47,16 @@ argument is set to point to the data in the packet. The .I struct pcap_pkthdr and the packet data are not to be freed by the caller, and are not guaranteed to be valid after the next call to -.BR pcap_next_ex() , -.BR pcap_next() , -.BR pcap_loop(3PCAP) , +.BR pcap_next_ex (), +.BR pcap_next (), +.BR pcap_loop (3PCAP), or -.BR pcap_dispatch(3PCAP) ; +.BR pcap_dispatch (3PCAP); if the code needs them to remain valid, it must make a copy of them. .PP -.B pcap_next() +.BR pcap_next () reads the next packet (by calling -.B pcap_dispatch() +.BR pcap_dispatch () with a .I cnt of 1) and returns a @@ -64,11 +64,11 @@ of 1) and returns a pointer to the data in that packet. The packet data is not to be freed by the caller, and is not guaranteed to be valid after the next call to -.BR pcap_next_ex() , -.BR pcap_next() , -.BR pcap_loop() , +.BR pcap_next_ex (), +.BR pcap_next (), +.BR pcap_loop (), or -.BR pcap_dispatch() ; +.BR pcap_dispatch (); if the code needs it to remain valid, it must make a copy of it. The .I pcap_pkthdr @@ -78,25 +78,25 @@ is filled in with the appropriate values for the packet. .PP The bytes of data from the packet begin with a link-layer header. The format of the link-layer header is indicated by the return value of the -.B pcap_datalink(PCAP) +.BR pcap_datalink (3PCAP) routine when handed the .B pcap_t value also passed to -.B pcap_loop() +.BR pcap_loop () or -.BR pcap_dispatch() . +.BR pcap_dispatch (). .I https://www.tcpdump.org/linktypes.html lists the values -.B pcap_datalink() +.BR pcap_datalink () can return and describes the packet formats that correspond to those values. The value it returns will be valid for all packets received unless and until -.B pcap_set_datalink(3PCAP) +.BR pcap_set_datalink (3PCAP) is called; after a successful call to -.BR pcap_set_datalink() , +.BR pcap_set_datalink (), all subsequent packets will have a link-layer header of the type specified by the link-layer header type value passed to -.BR pcap_set_datalink() . +.BR pcap_set_datalink (). .PP Do .B NOT @@ -106,30 +106,39 @@ any given link-layer header type, such as for Ethernet. For example, the "any" device on Linux will have a link-layer header type of .B DLT_LINUX_SLL +or +.B DLT_LINUX_SLL2 even if all devices on the system at the time the "any" device is opened have some other data link type, such as .B DLT_EN10MB for Ethernet. .SH RETURN VALUE -.B pcap_next_ex() -returns 1 if the packet was read without problems, 0 if packets are +.BR pcap_next_ex () +returns +.B 1 +if the packet was read without problems, +.B 0 +if packets are being read from a live capture and the packet buffer timeout expired, -.B PCAP_ERROR -if an error occurred while reading the packet, and .B PCAP_ERROR_BREAK if packets are being read from a ``savefile'' and there are no more packets to read -from the savefile. If +from the savefile, +.B PCAP_ERROR_NOT_ACTIVATED +if called on a capture handle that has been created but not activated, +or +.B PCAP_ERROR +if an error occurred while reading the packet. If .B PCAP_ERROR is returned, -.B pcap_geterr(3PCAP) +.BR pcap_geterr (3PCAP) or -.B pcap_perror(3PCAP) +.BR pcap_perror (3PCAP) may be called with .I p as an argument to fetch or display the error text. .PP -.B pcap_next() +.BR pcap_next () returns a pointer to the packet data on success, and returns .B NULL if an error occurred, or if no packets were read from a live capture @@ -141,4 +150,4 @@ non-blocking mode and no packets were available to be read), or if no more packets are available in a ``savefile.'' Unfortunately, there is no way to determine whether an error occurred or not. .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap_offline_filter.3pcap b/pcap_offline_filter.3pcap index 724f8366b496..dbb6e96656df 100644 --- a/pcap_offline_filter.3pcap +++ b/pcap_offline_filter.3pcap @@ -33,13 +33,13 @@ const struct pcap_pkthdr *h, const u_char *pkt) .ft .fi .SH DESCRIPTION -.B pcap_offline_filter() +.BR pcap_offline_filter () checks whether a filter matches a packet. .I fp is a pointer to a .I bpf_program struct, usually the result of a call to -.BR pcap_compile(3PCAP) . +.BR pcap_compile (3PCAP). .I h points to the .I pcap_pkthdr @@ -47,9 +47,9 @@ structure for the packet, and .I pkt points to the data in the packet. .SH RETURN VALUE -.B pcap_offline_filter() +.BR pcap_offline_filter () returns the return value of the filter program. This will be zero if the packet doesn't match the filter and non-zero if the packet matches the filter. .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap_open_dead.3pcap.in b/pcap_open_dead.3pcap.in index 97a97f3ac8c0..ced7d6cf1c3e 100644 --- a/pcap_open_dead.3pcap.in +++ b/pcap_open_dead.3pcap.in @@ -35,18 +35,18 @@ pcap_t *pcap_open_dead_with_tstamp_precision(int linktype, int snaplen, .fi .SH DESCRIPTION .PP -.B pcap_open_dead() +.BR pcap_open_dead () and -.B pcap_open_dead_with_tstamp_precision() +.BR pcap_open_dead_with_tstamp_precision () are used for creating a .B pcap_t structure to use when calling the other functions in libpcap. It is typically used when just using libpcap for compiling BPF code; it can also be used if using -.BR pcap_dump_open(3PCAP) , -.BR pcap_dump(3PCAP) , +.BR pcap_dump_open (3PCAP), +.BR pcap_dump (3PCAP), and -.B pcap_dump_close(3PCAP) +.BR pcap_dump_close (3PCAP) to write a savefile if there is no .B pcap_t that supplies the packets to be written. @@ -60,11 +60,11 @@ specifies the snapshot length for the .BR pcap_t . .PP When -.BR pcap_open_dead_with_tstamp_precision() , +.BR pcap_open_dead_with_tstamp_precision (), is used to create a .B pcap_t for use with -.BR pcap_dump_open() , +.BR pcap_dump_open (), .I precision specifies the time stamp precision for packets; .B PCAP_TSTAMP_PRECISION_MICRO @@ -73,6 +73,13 @@ seconds and microseconds, and .B PCAP_TSTAMP_PRECISION_NANO should be specified if the packets to be written have time stamps in seconds and nanoseconds. Its value does not affect -.BR pcap_compile(3PCAP) . +.BR pcap_compile (3PCAP). +.SH BACKWARD COMPATIBILITY +The +.BR pcap_open_dead_with_tstamp_precision () +function became available in libpcap release 1.5.1. In previous +releases, there was no mechanism to open a savefile for writing with +time stamps given in seconds and nanoseconds. .SH SEE ALSO -pcap(3PCAP), \%pcap-linktype(@MAN_MISC_INFO@) +.BR pcap (3PCAP), +.BR \%pcap-linktype (@MAN_MISC_INFO@) diff --git a/pcap_open_live.3pcap b/pcap_open_live.3pcap index 3286e294c425..b8c17299fc02 100644 --- a/pcap_open_live.3pcap +++ b/pcap_open_live.3pcap @@ -38,7 +38,7 @@ int promisc, int to_ms, char *errbuf); .ft .fi .SH DESCRIPTION -.B pcap_open_live() +.BR pcap_open_live () is used to obtain a packet capture handle to look at packets on the network. .I device @@ -53,7 +53,10 @@ can be used to capture packets from all interfaces. specifies the snapshot length to be set on the handle. .PP .I promisc -specifies if the interface is to be put into promiscuous mode. +specifies whether the interface is to be put into promiscuous mode. +If +.I promisc +is non-zero, promiscuous mode will be set, otherwise it will not be set. .PP .I to_ms specifies the packet buffer timeout, as a non-negative value, in @@ -61,7 +64,7 @@ milliseconds. (See .BR pcap (3PCAP) for an explanation of the packet buffer timeout.) .SH RETURN VALUE -.B pcap_open_live() +.BR pcap_open_live () returns a .I pcap_t * on success and @@ -74,11 +77,11 @@ is returned, is filled in with an appropriate error message. .I errbuf may also be set to warning text when -.B pcap_open_live() +.BR pcap_open_live () succeeds; to detect this case the caller should store a zero-length string in .I errbuf before calling -.B pcap_open_live() +.BR pcap_open_live () and display the warning to the user if .I errbuf is no longer a zero-length string. @@ -87,4 +90,5 @@ is assumed to be able to hold at least .B PCAP_ERRBUF_SIZE chars. .SH SEE ALSO -pcap_create(3PCAP), pcap_activate(3PCAP) +.BR pcap_create (3PCAP), +.BR pcap_activate (3PCAP) diff --git a/pcap_open_offline.3pcap.in b/pcap_open_offline.3pcap.in index 2bfbbace202e..e48bf25eb604 100644 --- a/pcap_open_offline.3pcap.in +++ b/pcap_open_offline.3pcap.in @@ -17,7 +17,7 @@ .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" -.TH PCAP_OPEN_OFFLINE 3PCAP "8 January 2018 " +.TH PCAP_OPEN_OFFLINE 3PCAP "23 August 2018" .SH NAME pcap_open_offline, pcap_open_offline_with_tstamp_precision, pcap_fopen_offline, pcap_fopen_offline_with_tstamp_precision \- open a saved capture file for reading @@ -42,15 +42,15 @@ pcap_t *pcap_fopen_offline_with_tstamp_precision(FILE *fp, .ft .fi .SH DESCRIPTION -.B pcap_open_offline() +.BR pcap_open_offline () and -.B pcap_open_offline_with_tstamp_precision() +.BR pcap_open_offline_with_tstamp_precision () are called to open a ``savefile'' for reading. .PP .I fname specifies the name of the file to open. The file can have the pcap file format as described in -.BR pcap-savefile (@MAN_FILE_FORMATS@), +.BR \%pcap-savefile (@MAN_FILE_FORMATS@), which is the file format used by, among other programs, .BR tcpdump (1) and @@ -60,7 +60,7 @@ be read. The name "-" is a synonym for .BR stdin . .PP -.B pcap_open_offline_with_tstamp_precision() +.BR pcap_open_offline_with_tstamp_precision () takes an additional .I precision argument specifying the time stamp precision desired; @@ -76,22 +76,22 @@ precision as the requested precision, they will be scaled up or down as necessary before being supplied. .PP Alternatively, you may call -.B pcap_fopen_offline() +.BR pcap_fopen_offline () or -.B pcap_fopen_offline_with_tstamp_precision() +.BR pcap_fopen_offline_with_tstamp_precision () to read dumped data from an existing open stream .IR fp . -.B pcap_fopen_offline_with_tstamp_precision() +.BR pcap_fopen_offline_with_tstamp_precision () takes an additional .I precision argument as described above. Note that on Windows, that stream should be opened in binary mode. .SH RETURN VALUE -.BR pcap_open_offline() , -.BR pcap_open_offline_with_tstamp_precision() , -.BR pcap_fopen_offline() , +.BR pcap_open_offline (), +.BR pcap_open_offline_with_tstamp_precision (), +.BR pcap_fopen_offline (), and -.B pcap_fopen_offline_with_tstamp_precision() +.BR pcap_fopen_offline_with_tstamp_precision () return a .I pcap_t * on success and @@ -107,10 +107,11 @@ is assumed to be able to hold at least .B PCAP_ERRBUF_SIZE chars. .SH BACKWARD COMPATIBILITY -.B pcap_open_offline_with_tstamp_precision +.BR pcap_open_offline_with_tstamp_precision () and -.B pcap_fopen_offline_with_tstamp_precision +.BR pcap_fopen_offline_with_tstamp_precision () became available in libpcap release 1.5.1. In previous releases, time stamps from a savefile are always given in seconds and microseconds. .SH SEE ALSO -pcap(3PCAP), pcap-savefile(@MAN_FILE_FORMATS@) +.BR pcap (3PCAP), +.BR \%pcap-savefile (@MAN_FILE_FORMATS@) diff --git a/pcap_set_buffer_size.3pcap b/pcap_set_buffer_size.3pcap index 684f739cc6b9..49492c068e2e 100644 --- a/pcap_set_buffer_size.3pcap +++ b/pcap_set_buffer_size.3pcap @@ -31,15 +31,19 @@ int pcap_set_buffer_size(pcap_t *p, int buffer_size); .ft .fi .SH DESCRIPTION -.B pcap_set_buffer_size() +.BR pcap_set_buffer_size () sets the buffer size that will be used on a capture handle when the handle is activated to .IR buffer_size , which is in units of bytes. .SH RETURN VALUE -.B pcap_set_buffer_size() -returns 0 on success or +.BR pcap_set_buffer_size () +returns +.B 0 +on success or .B PCAP_ERROR_ACTIVATED if called on a capture handle that has been activated. .SH SEE ALSO -pcap(3PCAP), pcap_create(3PCAP), pcap_activate(3PCAP) +.BR pcap (3PCAP), +.BR pcap_create (3PCAP), +.BR pcap_activate (3PCAP) diff --git a/pcap_set_datalink.3pcap b/pcap_set_datalink.3pcap index 66cfdb1e7a9a..fd9079131672 100644 --- a/pcap_set_datalink.3pcap +++ b/pcap_set_datalink.3pcap @@ -17,7 +17,7 @@ .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" -.TH PCAP_SET_DATALINK 3PCAP "25 July 2018" +.TH PCAP_SET_DATALINK 3PCAP "5 March 2022" .SH NAME pcap_set_datalink \- set the link-layer header type to be used by a capture device @@ -32,22 +32,27 @@ int pcap_set_datalink(pcap_t *p, int dlt); .ft .fi .SH DESCRIPTION -.B pcap_set_datalink() +.BR pcap_set_datalink () is used to set the current link-layer header type of the pcap descriptor to the type specified by .IR dlt . .SH RETURN VALUE -.B pcap_set_datalink() -returns 0 on success and +.BR pcap_set_datalink () +returns +.B 0 +on success, +.B PCAP_ERROR_NOT_ACTIVATED +if called on a capture handle that has been created but not activated, or .B PCAP_ERROR -on failure. If +on other errors. If .B PCAP_ERROR is returned, -.B pcap_geterr(3PCAP) +.BR pcap_geterr (3PCAP) or -.B pcap_perror(3PCAP) +.BR pcap_perror (3PCAP) may be called with .I p as an argument to fetch or display the error text. .SH SEE ALSO -pcap(3PCAP), pcap_datalink_name_to_val(3PCAP) +.BR pcap (3PCAP), +.BR pcap_datalink_name_to_val (3PCAP) diff --git a/pcap_set_immediate_mode.3pcap.in b/pcap_set_immediate_mode.3pcap.in index 2fe45c5439f2..95c9cffbded7 100644 --- a/pcap_set_immediate_mode.3pcap.in +++ b/pcap_set_immediate_mode.3pcap.in @@ -18,7 +18,7 @@ .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" -.TH PCAP_SET_IMMEDIATE_MODE 3PCAP "22 August 2018" +.TH PCAP_SET_IMMEDIATE_MODE 3PCAP "23 August 2018" .SH NAME pcap_set_immediate_mode \- set immediate mode for a not-yet-activated capture handle @@ -32,7 +32,7 @@ int pcap_set_immediate_mode(pcap_t *p, int immediate_mode); .ft .fi .SH DESCRIPTION -.B pcap_set_immediate_mode() +.BR pcap_set_immediate_mode () sets whether immediate mode should be set on a capture handle when the handle is activated. In immediate mode, packets are always delivered as soon as they arrive, with no buffering. @@ -40,8 +40,10 @@ If .I immediate_mode is non-zero, immediate mode will be set, otherwise it will not be set. .SH RETURN VALUE -.B pcap_set_immediate_mode() -returns 0 on success or +.BR pcap_set_immediate_mode () +returns +.B 0 +on success or .B PCAP_ERROR_ACTIVATED if called on a capture handle that has been activated. .SH BACKWARD COMPATIBILITY @@ -54,11 +56,11 @@ immediate mode must be turned on with a .B BIOCIMMEDIATE .BR ioctl (2), as documented in -.BR bpf(@MAN_DEVICES@) , +.BR bpf (@MAN_DEVICES@), on the descriptor returned by -.B pcap_fileno(3PCAP), +.BR pcap_fileno (3PCAP), after -.BR pcap_activate(3PCAP) +.BR pcap_activate (3PCAP) is called; .IP on Solaris 10 and earlier versions of Solaris, immediate mode must be @@ -68,28 +70,30 @@ don't assume it's sufficient); .IP on Digital UNIX/Tru64 UNIX, immediate mode must be turned on by doing a .B BIOCMBIC -.BR ioctl , +.BR ioctl (), as documented in -.BR packetfilter(7) , +.BR packetfilter (7), to clear the .B ENBATCH flag on the descriptor returned by -.B pcap_fileno(3PCAP), +.BR pcap_fileno (3PCAP), after -.BR pcap_activate(3PCAP) +.BR pcap_activate (3PCAP) is called; .IP on Windows, immediate mode must be turned on by calling -.B pcap_setmintocopy() +.BR pcap_setmintocopy () with a size of 0. .PP On Linux, with previous releases of libpcap, capture devices are always in immediate mode; however, in 1.5.0 and later, they are, by default, .B not in immediate mode, so if -.B pcap_set_immediate_mode() +.BR pcap_set_immediate_mode () is available, it should be used. .PP On other platforms, capture devices are always in immediate mode. .SH SEE ALSO -pcap(3PCAP), pcap_create(3PCAP), pcap_activate(3PCAP) +.BR pcap (3PCAP), +.BR pcap_create (3PCAP), +.BR pcap_activate (3PCAP) diff --git a/pcap_set_promisc.3pcap b/pcap_set_promisc.3pcap index fcd797a34c50..cfc6cddafebd 100644 --- a/pcap_set_promisc.3pcap +++ b/pcap_set_promisc.3pcap @@ -31,16 +31,20 @@ int pcap_set_promisc(pcap_t *p, int promisc); .ft .fi .SH DESCRIPTION -.B pcap_set_promisc() +.BR pcap_set_promisc () sets whether promiscuous mode should be set on a capture handle when the handle is activated. If .I promisc is non-zero, promiscuous mode will be set, otherwise it will not be set. .SH RETURN VALUE -.B pcap_set_promisc() -returns 0 on success or +.BR pcap_set_promisc () +returns +.B 0 +on success or .B PCAP_ERROR_ACTIVATED if called on a capture handle that has been activated. .SH SEE ALSO -pcap(3PCAP), pcap_create(3PCAP), pcap_activate(3PCAP) +.BR pcap (3PCAP), +.BR pcap_create (3PCAP), +.BR pcap_activate (3PCAP) diff --git a/pcap_set_protocol_linux.3pcap b/pcap_set_protocol_linux.3pcap index 873017ba9886..a891d7492aec 100644 --- a/pcap_set_protocol_linux.3pcap +++ b/pcap_set_protocol_linux.3pcap @@ -17,7 +17,7 @@ .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" -.TH PCAP_SET_PROTOCOL_LINUX 3PCAP "24 August 2017" +.TH PCAP_SET_PROTOCOL_LINUX 3PCAP "22 August 2018" .SH NAME pcap_set_protocol_linux \- set capture protocol for a not-yet-activated capture handle @@ -32,7 +32,7 @@ int pcap_set_protocol_linux(pcap_t *p, int protocol); .fi .SH DESCRIPTION On network interface devices on Linux, -.B pcap_set_protocol_linux() +.BR pcap_set_protocol_linux () sets the protocol to be used in the .BR socket (2) call to create a capture socket when the handle is activated. The @@ -48,21 +48,25 @@ other than a network interface, it will have no effect. .LP It should not be used in portable code; instead, a filter should be specified with -.BR pcap_setfilter(3PCAP) . +.BR pcap_setfilter (3PCAP). .LP If a given network interface provides a standard link-layer header, with a standard packet type, but provides some packet types with a different socket-layer protocol type from the one in the link-layer header, that packet type cannot be filtered with a filter specified with -.B pcap_setfilter() +.BR pcap_setfilter () but can be filtered by specifying the socket-layer protocol type using -.BR pcap_set_protocol_linux() . +.BR pcap_set_protocol_linux (). .SH RETURN VALUE -.B pcap_set_protocol_linux() -returns 0 on success or +.BR pcap_set_protocol_linux () +returns +.B 0 +on success or .B PCAP_ERROR_ACTIVATED if called on a capture handle that has been activated. .SH BACKWARD COMPATIBILITY This function became available in libpcap release 1.9.0. .SH SEE ALSO -pcap(3PCAP), pcap_create(3PCAP), pcap_activate(3PCAP) +.BR pcap (3PCAP), +.BR pcap_create (3PCAP), +.BR pcap_activate (3PCAP) diff --git a/pcap_set_rfmon.3pcap b/pcap_set_rfmon.3pcap index 691518a5b28c..2b104c25574a 100644 --- a/pcap_set_rfmon.3pcap +++ b/pcap_set_rfmon.3pcap @@ -31,17 +31,21 @@ int pcap_set_rfmon(pcap_t *p, int rfmon); .ft .fi .SH DESCRIPTION -.B pcap_set_rfmon() +.BR pcap_set_rfmon () sets whether monitor mode should be set on a capture handle when the handle is activated. If .I rfmon is non-zero, monitor mode will be set, otherwise it will not be set. .SH RETURN VALUE -.B pcap_set_rfmon() -returns 0 on success or +.BR pcap_set_rfmon () +returns +.B 0 +on success or .B PCAP_ERROR_ACTIVATED if called on a capture handle that has been activated. .SH SEE ALSO -pcap(3PCAP), pcap_create(3PCAP), pcap_activate(3PCAP), -pcap_can_set_rfmon(3PCAP) +.BR pcap (3PCAP), +.BR pcap_create (3PCAP), +.BR pcap_activate (3PCAP), +.BR pcap_can_set_rfmon (3PCAP) diff --git a/pcap_set_snaplen.3pcap b/pcap_set_snaplen.3pcap index 44eb15488a11..e3acff158f91 100644 --- a/pcap_set_snaplen.3pcap +++ b/pcap_set_snaplen.3pcap @@ -31,14 +31,18 @@ int pcap_set_snaplen(pcap_t *p, int snaplen); .ft .fi .SH DESCRIPTION -.B pcap_set_snaplen() +.BR pcap_set_snaplen () sets the snapshot length to be used on a capture handle when the handle is activated to .IR snaplen . .SH RETURN VALUE -.B pcap_set_snaplen() -returns 0 on success or +.BR pcap_set_snaplen () +returns +.B 0 +on success or .B PCAP_ERROR_ACTIVATED if called on a capture handle that has been activated. .SH SEE ALSO -pcap(3PCAP), pcap_create(3PCAP), pcap_activate(3PCAP) +.BR pcap (3PCAP), +.BR pcap_create (3PCAP), +.BR pcap_activate (3PCAP) diff --git a/pcap_set_timeout.3pcap b/pcap_set_timeout.3pcap index e67b8132f732..d909b2c4445e 100644 --- a/pcap_set_timeout.3pcap +++ b/pcap_set_timeout.3pcap @@ -31,7 +31,7 @@ int pcap_set_timeout(pcap_t *p, int to_ms); .ft .fi .SH DESCRIPTION -.B pcap_set_timeout() +.BR pcap_set_timeout () sets the packet buffer timeout that will be used on a capture handle when the handle is activated to .IR to_ms , @@ -44,10 +44,13 @@ behavior if the timeout is set to zero or to a negative value. We recommend always setting the timeout to a non-zero value unless immediate mode is set, in which case the timeout has no effect. .SH RETURN VALUE -.B pcap_set_timeout() -returns 0 on success or +.BR pcap_set_timeout () +returns +.B 0 +on success or .B PCAP_ERROR_ACTIVATED if called on a capture handle that has been activated. .SH SEE ALSO -pcap_create(3PCAP), pcap_activate(3PCAP), -\%pcap_set_immediate_mode(3PCAP) +.BR pcap_create (3PCAP), +.BR pcap_activate (3PCAP), +.BR \%pcap_set_immediate_mode (3PCAP) diff --git a/pcap_set_tstamp_precision.3pcap.in b/pcap_set_tstamp_precision.3pcap.in index dc2b4b3d2a5e..1889f43dffae 100644 --- a/pcap_set_tstamp_precision.3pcap.in +++ b/pcap_set_tstamp_precision.3pcap.in @@ -19,7 +19,7 @@ .\"IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED .\"WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -.TH PCAP_SET_TSTAMP_PRECISION 3PCAP "5 February 2015" +.TH PCAP_SET_TSTAMP_PRECISION 3PCAP "23 August 2018" .SH NAME pcap_set_tstamp_precision \- set the time stamp precision returned in captures @@ -34,22 +34,24 @@ int pcap_set_tstamp_precision(pcap_t *p, int tstamp_precision); .ft .fi .SH DESCRIPTION -.B pcap_set_tstamp_precision() +.BR pcap_set_tstamp_precision () sets the precision of the time stamp desired for packets captured on the pcap descriptor to the type specified by .IR tstamp_precision . It must be called on a pcap descriptor created by -.B pcap_create(3PCAP) +.BR pcap_create (3PCAP) that has not yet been activated by -.BR pcap_activate(3PCAP) . +.BR pcap_activate (3PCAP). Two time stamp precisions are supported, microseconds and nanoseconds. One can use options .B PCAP_TSTAMP_PRECISION_MICRO and .B PCAP_TSTAMP_PRECISION_NANO to request desired precision. By default, time stamps are in microseconds. .SH RETURN VALUE -.B pcap_set_tstamp_precision() -returns 0 on success if the specified time stamp precision is expected to be +.BR pcap_set_tstamp_precision () +returns +.B 0 +on success if the specified time stamp precision is expected to be supported by the capture device, .B PCAP_ERROR_TSTAMP_PRECISION_NOTSUP if the capture device does not support the requested time stamp @@ -61,6 +63,6 @@ This function became available in libpcap release 1.5.1. In previous releases, time stamps from a capture device or savefile are always given in seconds and microseconds. .SH SEE ALSO -pcap(3PCAP), -pcap_get_tstamp_precision(3PCAP), -pcap-tstamp(@MAN_MISC_INFO@) +.BR pcap (3PCAP), +.BR pcap_get_tstamp_precision (3PCAP), +.BR \%pcap-tstamp (@MAN_MISC_INFO@) diff --git a/pcap_set_tstamp_type.3pcap.in b/pcap_set_tstamp_type.3pcap.in index 9833f46a3c43..cd2dc71ced7d 100644 --- a/pcap_set_tstamp_type.3pcap.in +++ b/pcap_set_tstamp_type.3pcap.in @@ -18,7 +18,7 @@ .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" -.TH PCAP_SET_TSTAMP_TYPE 3PCAP "22 August 2018" +.TH PCAP_SET_TSTAMP_TYPE 3PCAP "8 September 2019" .SH NAME pcap_set_tstamp_type \- set the time stamp type to be used by a capture device @@ -33,23 +33,25 @@ int pcap_set_tstamp_type(pcap_t *p, int tstamp_type); .ft .fi .SH DESCRIPTION -.B pcap_set_tstamp_type() +.BR pcap_set_tstamp_type () sets the type of time stamp desired for packets captured on the pcap descriptor to the type specified by .IR tstamp_type . It must be called on a pcap descriptor created by -.B pcap_create(3PCAP) +.BR pcap_create (3PCAP) that has not yet been activated by -.BR pcap_activate(3PCAP) . -.B pcap_list_tstamp_types(3PCAP) +.BR pcap_activate (3PCAP). +.BR pcap_list_tstamp_types (3PCAP) will give a list of the time stamp types supported by a given capture device. See -.BR pcap-tstamp (@MAN_MISC_INFO@) +.BR \%pcap-tstamp (@MAN_MISC_INFO@) for a list of all the time stamp types. .SH RETURN VALUE -.B pcap_set_tstamp_type() -returns 0 on success if the specified time stamp type is expected to be +.BR pcap_set_tstamp_type () +returns +.B 0 +on success if the specified time stamp type is expected to be supported by the capture device, .B PCAP_WARNING_TSTAMP_TYPE_NOTSUP if the specified time stamp type is not supported by the @@ -66,5 +68,5 @@ This function became available in libpcap release 1.2.1. In previous releases, the time stamp type cannot be set; only the default time stamp type offered by a capture source is available. .SH SEE ALSO -pcap(3PCAP), -pcap_tstamp_type_name_to_val(3PCAP) +.BR pcap (3PCAP), +.BR pcap_tstamp_type_name_to_val (3PCAP) diff --git a/pcap_setdirection.3pcap b/pcap_setdirection.3pcap index f174b98f9024..04278e84848f 100644 --- a/pcap_setdirection.3pcap +++ b/pcap_setdirection.3pcap @@ -17,7 +17,7 @@ .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" -.TH PCAP_SETDIRECTION 3PCAP "25 July 2018" +.TH PCAP_SETDIRECTION 3PCAP "5 March 2022" .SH NAME pcap_setdirection \- set the direction for which packets will be captured .SH SYNOPSIS @@ -31,7 +31,7 @@ int pcap_setdirection(pcap_t *p, pcap_direction_t d); .ft .fi .SH DESCRIPTION -.B pcap_setdirection() +.BR pcap_setdirection () is used to specify a direction that packets will be captured. .I d is one of the constants @@ -48,7 +48,7 @@ will capture packets received by or sent by the device. .B PCAP_D_INOUT is the default setting if this function is not called. .PP -.B pcap_setdirection() +.BR pcap_setdirection () isn't necessarily fully supported on all platforms; some platforms might return an error for all values, and some other platforms might not support @@ -56,17 +56,21 @@ support .PP This operation is not supported if a ``savefile'' is being read. .SH RETURN VALUE -.B pcap_setdirection() -returns 0 on success and +.BR pcap_setdirection () +returns +.B 0 +on success, +.B PCAP_ERROR_NOT_ACTIVATED +if called on a capture handle that has been created but not activated, or .B PCAP_ERROR -on failure. If +on other errors. If .B PCAP_ERROR is returned, -.B pcap_geterr(3PCAP) +.BR pcap_geterr (3PCAP) or -.B pcap_perror(3PCAP) +.BR pcap_perror (3PCAP) may be called with .I p as an argument to fetch or display the error text. .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap_setfilter.3pcap b/pcap_setfilter.3pcap index 872969398469..e063ae0ea2fb 100644 --- a/pcap_setfilter.3pcap +++ b/pcap_setfilter.3pcap @@ -17,7 +17,7 @@ .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" -.TH PCAP_SETFILTER 3PCAP "25 July 2018" +.TH PCAP_SETFILTER 3PCAP "5 March 2022" .SH NAME pcap_setfilter \- set the filter .SH SYNOPSIS @@ -31,25 +31,29 @@ int pcap_setfilter(pcap_t *p, struct bpf_program *fp); .ft .fi .SH DESCRIPTION -.B pcap_setfilter() +.BR pcap_setfilter () is used to specify a filter program. .I fp is a pointer to a .I bpf_program struct, usually the result of a call to -.BR \%pcap_compile(3PCAP) . +.BR \%pcap_compile (3PCAP). .SH RETURN VALUE -.B pcap_setfilter() -returns 0 on success and +.BR pcap_setfilter () +returns +.B 0 +on success, +.B PCAP_ERROR_NOT_ACTIVATED +if called on a capture handle that has been created but not activated, or .B PCAP_ERROR -on failure. If +on other errors. If .B PCAP_ERROR is returned, -.B pcap_geterr(3PCAP) +.BR pcap_geterr (3PCAP) or -.B pcap_perror(3PCAP) +.BR pcap_perror (3PCAP) may be called with .I p as an argument to fetch or display the error text. .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap_setnonblock.3pcap b/pcap_setnonblock.3pcap index e8adebea0fa2..1f3364747597 100644 --- a/pcap_setnonblock.3pcap +++ b/pcap_setnonblock.3pcap @@ -17,7 +17,7 @@ .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" -.TH PCAP_SETNONBLOCK 3PCAP "25 July 2018" +.TH PCAP_SETNONBLOCK 3PCAP "5 March 2022" .SH NAME pcap_setnonblock, pcap_getnonblock \- set or get the state of non-blocking mode on a capture device @@ -38,7 +38,7 @@ int pcap_getnonblock(pcap_t *p, char *errbuf); .ft .fi .SH DESCRIPTION -.B pcap_setnonblock() +.BR pcap_setnonblock () puts a capture handle into ``non-blocking'' mode, or takes it out of ``non-blocking'' mode, depending on whether the .I nonblock @@ -47,31 +47,50 @@ If there is an error, .B PCAP_ERROR is returned and .I errbuf -is filled in with an appropriate error message; otherwise, 0 is -returned. +is filled in with an appropriate error message; otherwise, +.B 0 +is returned. +.PP In ``non-blocking'' mode, an attempt to read from the capture descriptor with -.B pcap_dispatch(3PCAP) -will, if no packets are currently available to be read, return 0 -immediately rather than blocking waiting for packets to arrive. -.B pcap_loop(3PCAP) +.BR pcap_dispatch (3PCAP) and -.B pcap_next(3PCAP) -will not work in ``non-blocking'' mode. +.BR pcap_next_ex (3PCAP) +will, if no packets are currently available to be read, return +.B 0 +immediately rather than blocking waiting for packets to arrive. +.PP +.BR pcap_loop (3PCAP) +will loop forever, consuming CPU time when no packets are currently +available; +.BR pcap_dispatch () +should be used instead. +.BR pcap_next (3PCAP) +will return +.B NULL +if there are no packets currently available to read; +this is indistinguishable from an error, so +.BR pcap_next_ex () +should be used instead. .PP When first activated with -.B pcap_activate(3PCAP) +.BR pcap_activate (3PCAP) or opened with -.B pcap_open_live(3PCAP) , +.BR pcap_open_live (3PCAP) , a capture handle is not in ``non-blocking mode''; a call to -.B pcap_setnonblock() +.BR pcap_setnonblock () is required in order to put it into ``non-blocking'' mode. .SH RETURN VALUE -.B pcap_getnonblock() +.BR pcap_getnonblock () returns the current ``non-blocking'' state of the capture descriptor; it -always returns 0 on ``savefiles''. -If there is an error, +always returns +.B 0 +on ``savefiles''. +If called on a capture handle that has been created but not activated, +.B PCAP_ERROR_NOT_ACTIVATED +is returned. +If there is another error, .B PCAP_ERROR is returned and .I errbuf @@ -82,4 +101,6 @@ is assumed to be able to hold at least .B PCAP_ERRBUF_SIZE chars. .SH SEE ALSO -pcap(3PCAP), pcap_next_ex(3PCAP), pcap_geterr(3PCAP) +.BR pcap (3PCAP), +.BR pcap_next_ex (3PCAP), +.BR pcap_geterr (3PCAP) diff --git a/pcap_snapshot.3pcap b/pcap_snapshot.3pcap index ee54bb0834fe..25f6e0367858 100644 --- a/pcap_snapshot.3pcap +++ b/pcap_snapshot.3pcap @@ -31,22 +31,22 @@ int pcap_snapshot(pcap_t *p); .ft .fi .SH DESCRIPTION -.B pcap_snapshot() +.BR pcap_snapshot () returns the snapshot length specified when -.B pcap_set_snaplen(3PCAP) +.BR pcap_set_snaplen (3PCAP) or -.B pcap_open_live(3PCAP) +.BR pcap_open_live (3PCAP) was called, for a live capture, or the snapshot length from the capture file, for a ``savefile''. .PP It must not be called on a pcap descriptor created by -.B \%pcap_create(3PCAP) +.BR \%pcap_create (3PCAP) that has not yet been activated by -.BR \%pcap_activate(3PCAP) . +.BR \%pcap_activate (3PCAP). .SH RETURN VALUE -.B pcap_snapshot() +.BR pcap_snapshot () returns the snapshot length on success and .B PCAP_ERROR_NOT_ACTIVATED if called on a capture handle that has been created but not activated. .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap_stats.3pcap b/pcap_stats.3pcap index 465dada48ea7..98be9bd7d308 100644 --- a/pcap_stats.3pcap +++ b/pcap_stats.3pcap @@ -17,7 +17,7 @@ .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" -.TH PCAP_STATS 3PCAP "25 July 2018" +.TH PCAP_STATS 3PCAP "5 March 2022" .SH NAME pcap_stats \- get capture statistics .SH SYNOPSIS @@ -31,13 +31,13 @@ int pcap_stats(pcap_t *p, struct pcap_stat *ps); .ft .fi .SH DESCRIPTION -.B pcap_stats() +.BR pcap_stats () fills in the .B struct pcap_stat pointed to by its second argument. The values represent packet statistics from the start of the run to the time of the call. .PP -.B pcap_stats() +.BR pcap_stats () is supported only on live captures, not on ``savefiles''; no statistics are stored in ``savefiles'', so no statistics are available when reading from a ``savefile''. @@ -82,19 +82,24 @@ no packets were dropped by the interface, or it might mean that the statistic is unavailable, so it should not be treated as an indication that the interface did not drop any packets. .SH RETURN VALUE -.B pcap_stats() -returns 0 on success and returns +.BR pcap_stats () +returns +.B 0 +on success, +.B PCAP_ERROR_NOT_ACTIVATED +if called on a capture handle that has been created but not activated, +or .B PCAP_ERROR -if there is an error or if +if there is another error or if .I p doesn't support packet statistics. If .B PCAP_ERROR is returned, -.B pcap_geterr(3PCAP) +.BR pcap_geterr (3PCAP) or -.B pcap_perror(3PCAP) +.BR pcap_perror (3PCAP) may be called with .I p as an argument to fetch or display the error text. .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap_statustostr.3pcap b/pcap_statustostr.3pcap index 9c2057ae4f95..c679e362e9b2 100644 --- a/pcap_statustostr.3pcap +++ b/pcap_statustostr.3pcap @@ -31,11 +31,11 @@ const char *pcap_statustostr(int error); .ft .fi .SH DESCRIPTION -.B pcap_statustostr() +.BR pcap_statustostr () converts a .B PCAP_ERROR_ or .B PCAP_WARNING_ value returned by a libpcap routine to an error string. .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap_strerror.3pcap b/pcap_strerror.3pcap index a5775f4229a5..dedfa40697cb 100644 --- a/pcap_strerror.3pcap +++ b/pcap_strerror.3pcap @@ -31,10 +31,10 @@ const char *pcap_strerror(int error); .ft .fi .SH DESCRIPTION -.B pcap_strerror() +.BR pcap_strerror () is provided in case .BR strerror (3) isn't available. It returns an error message string corresponding to .IR error . .SH SEE ALSO -pcap(3PCAP) +.BR pcap (3PCAP) diff --git a/pcap_tstamp_type_name_to_val.3pcap b/pcap_tstamp_type_name_to_val.3pcap index fdcc6c6d8d62..f60516e7e59d 100644 --- a/pcap_tstamp_type_name_to_val.3pcap +++ b/pcap_tstamp_type_name_to_val.3pcap @@ -33,11 +33,11 @@ int pcap_tstamp_type_name_to_val(const char *name); .ft .fi .SH DESCRIPTION -.B pcap_tstamp_type_name_to_val() +.BR pcap_tstamp_type_name_to_val () translates a time stamp type name to the corresponding time stamp type value. The translation is case-insensitive. .SH RETURN VALUE -.B pcap_tstamp_type_name_to_val() +.BR pcap_tstamp_type_name_to_val () returns time stamp type value on success and .B PCAP_ERROR on failure. @@ -45,4 +45,5 @@ on failure. .PP This function became available in libpcap release 1.2.1. .SH SEE ALSO -pcap(3PCAP), pcap_tstamp_type_val_to_name(3PCAP) +.BR pcap (3PCAP), +.BR pcap_tstamp_type_val_to_name (3PCAP) diff --git a/pcap_tstamp_type_val_to_name.3pcap b/pcap_tstamp_type_val_to_name.3pcap index 9374f489c55b..5958e8102584 100644 --- a/pcap_tstamp_type_val_to_name.3pcap +++ b/pcap_tstamp_type_val_to_name.3pcap @@ -34,13 +34,13 @@ const char *pcap_tstamp_type_val_to_description(int tstamp_type); .ft .fi .SH DESCRIPTION -.B pcap_tstamp_type_val_to_name() +.BR pcap_tstamp_type_val_to_name () translates a time stamp type value to the corresponding time stamp type name. .B NULL is returned on failure. .PP -.B pcap_tstamp_type_val_to_description() +.BR pcap_tstamp_type_val_to_description () translates a time stamp type value to a short description of that time stamp type. .B NULL @@ -49,4 +49,5 @@ is returned on failure. .PP These functions became available in libpcap release 1.2.1. .SH SEE ALSO -pcap(3PCAP), pcap_tstamp_type_name_to_val(3PCAP) +.BR pcap (3PCAP), +.BR pcap_tstamp_type_name_to_val (3PCAP) diff --git a/pflog.h b/pflog.h new file mode 100644 index 000000000000..b49d04fc8ac0 --- /dev/null +++ b/pflog.h @@ -0,0 +1,157 @@ +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * pflog headers, at least as they exist now. + */ +#define PFLOG_IFNAMSIZ 16 +#define PFLOG_RULESET_NAME_SIZE 16 + +/* + * Direction values. + */ +#define PF_INOUT 0 +#define PF_IN 1 +#define PF_OUT 2 +#if defined(__OpenBSD__) +#define PF_FWD 3 +#endif + +/* + * Reason values. + */ +#define PFRES_MATCH 0 +#define PFRES_BADOFF 1 +#define PFRES_FRAG 2 +#define PFRES_SHORT 3 +#define PFRES_NORM 4 +#define PFRES_MEMORY 5 +#define PFRES_TS 6 +#define PFRES_CONGEST 7 +#define PFRES_IPOPTIONS 8 +#define PFRES_PROTCKSUM 9 +#define PFRES_BADSTATE 10 +#define PFRES_STATEINS 11 +#define PFRES_MAXSTATES 12 +#define PFRES_SRCLIMIT 13 +#define PFRES_SYNPROXY 14 +#if defined(__FreeBSD__) +#define PFRES_MAPFAILED 15 +#elif defined(__NetBSD__) +#define PFRES_STATELOCKED 15 +#elif defined(__OpenBSD__) +#define PFRES_TRANSLATE 15 +#define PFRES_NOROUTE 16 +#elif defined(__APPLE__) +#define PFRES_DUMMYNET 15 +#endif + +/* + * Action values. + */ +#define PF_PASS 0 +#define PF_DROP 1 +#define PF_SCRUB 2 +#define PF_NOSCRUB 3 +#define PF_NAT 4 +#define PF_NONAT 5 +#define PF_BINAT 6 +#define PF_NOBINAT 7 +#define PF_RDR 8 +#define PF_NORDR 9 +#define PF_SYNPROXY_DROP 10 +#if defined(__FreeBSD__) +#define PF_DEFER 11 +#elif defined(__OpenBSD__) +#define PF_DEFER 11 +#define PF_MATCH 12 +#define PF_DIVERT 13 +#define PF_RT 14 +#define PF_AFRT 15 +#elif defined(__APPLE__) +#define PF_DUMMYNET 11 +#define PF_NODUMMYNET 12 +#define PF_NAT64 13 +#define PF_NONAT64 14 +#endif + +struct pf_addr { + union { + struct in_addr v4; + struct in6_addr v6; + uint8_t addr8[16]; + uint16_t addr16[8]; + uint32_t addr32[4]; + } pfa; /* 128-bit address */ +#define v4 pfa.v4 +#define v6 pfa.v6 +#define addr8 pfa.addr8 +#define addr16 pfa.addr16 +#define addr32 pfa.addr32 +}; + +struct pfloghdr { + uint8_t length; + uint8_t af; + uint8_t action; + uint8_t reason; + char ifname[PFLOG_IFNAMSIZ]; + char ruleset[PFLOG_RULESET_NAME_SIZE]; + uint32_t rulenr; + uint32_t subrulenr; + uint32_t uid; + int32_t pid; + uint32_t rule_uid; + int32_t rule_pid; + uint8_t dir; +#if defined(__OpenBSD__) + uint8_t rewritten; + uint8_t naf; + uint8_t pad[1]; +#else + uint8_t pad[3]; +#endif +#if defined(__FreeBSD__) + uint32_t ridentifier; + uint8_t reserve; + uint8_t pad2[3]; +#elif defined(__OpenBSD__) + struct pf_addr saddr; + struct pf_addr daddr; + uint16_t sport; + uint16_t dport; +#endif +}; + + + diff --git a/portability.h b/portability.h index 543846e8bd25..84d0778a5c77 100644 --- a/portability.h +++ b/portability.h @@ -52,7 +52,7 @@ extern "C" { #if defined(_MSC_VER) || defined(__MINGW32__) /* * strncat_s() is supported at least back to Visual - * Studio 2005. + * Studio 2005; we require Visual Studio 2015 or later. */ #define pcap_strlcat(x, y, z) \ strncat_s((x), (z), (y), _TRUNCATE) @@ -70,7 +70,7 @@ extern "C" { #if defined(_MSC_VER) || defined(__MINGW32__) /* * strncpy_s() is supported at least back to Visual - * Studio 2005. + * Studio 2005; we require Visual Studio 2015 or later. */ #define pcap_strlcpy(x, y, z) \ strncpy_s((x), (z), (y), _TRUNCATE) @@ -83,8 +83,6 @@ extern "C" { #endif #ifdef _MSC_VER - #define isascii __isascii - /* * If has been included, and _DEBUG is defined, and * __STDC__ is zero, will define strdup() to call @@ -97,43 +95,9 @@ extern "C" { #endif /* - * On Windows, snprintf(), with that name and with C99 behavior - i.e., - * guaranteeing that the formatted string is null-terminated - didn't - * appear until Visual Studio 2015. Prior to that, the C runtime had - * only _snprintf(), which *doesn't* guarantee that the string is - * null-terminated if it is truncated due to the buffer being too - * small. We therefore can't just define snprintf to be _snprintf - * and define vsnprintf to be _vsnprintf, as we're relying on null- - * termination of strings in all cases. - * - * We also want to allow this to be built with versions of Visual Studio - * prior to VS 2015, so we can't rely on snprintf() being present. - * - * And we want to make sure that, if we support plugins in the future, - * a routine with C99 snprintf() behavior will be available to them. - * We also don't want it to collide with the C library snprintf() if - * there is one. - * - * So we make pcap_snprintf() and pcap_vsnprintf() available, either by - * #defining them to be snprintf or vsnprintf, respectively, or by - * defining our own versions and exporting them. - */ -#ifdef HAVE_SNPRINTF -#define pcap_snprintf snprintf -#else -extern int pcap_snprintf(char *, size_t, PCAP_FORMAT_STRING(const char *), ...) - PCAP_PRINTFLIKE(3, 4); -#endif - -#ifdef HAVE_VSNPRINTF -#define pcap_vsnprintf vsnprintf -#else -extern int pcap_vsnprintf(char *, size_t, const char *, va_list ap); -#endif - -/* - * We also want asprintf(), for some cases where we use it to construct - * dynamically-allocated variable-length strings. + * We want asprintf(), for some cases where we use it to construct + * dynamically-allocated variable-length strings; it's present on + * some, but not all, platforms. */ #ifdef HAVE_ASPRINTF #define pcap_asprintf asprintf @@ -148,6 +112,30 @@ extern int pcap_asprintf(char **, PCAP_FORMAT_STRING(const char *), ...) extern int pcap_vasprintf(char **, const char *, va_list ap); #endif +/* For Solaris before 11. */ +#ifndef timeradd +#define timeradd(a, b, result) \ + do { \ + (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \ + (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \ + if ((result)->tv_usec >= 1000000) { \ + ++(result)->tv_sec; \ + (result)->tv_usec -= 1000000; \ + } \ + } while (0) +#endif /* timeradd */ +#ifndef timersub +#define timersub(a, b, result) \ + do { \ + (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ + (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ + if ((result)->tv_usec < 0) { \ + --(result)->tv_sec; \ + (result)->tv_usec += 1000000; \ + } \ + } while (0) +#endif /* timersub */ + #ifdef HAVE_STRTOK_R #define pcap_strtok_r strtok_r #else diff --git a/rpcap-protocol.c b/rpcap-protocol.c index 692f7c5c0d6b..0cdc0ba323b7 100644 --- a/rpcap-protocol.c +++ b/rpcap-protocol.c @@ -61,6 +61,8 @@ * * \param sock: the socket we are currently using. * + * \param ssl: if compiled with openssl, the optional ssl handler to use with the above socket. + * * \param ver: the protocol version we want to put in the reply. * * \param errcode: a integer which tells the other party the type of error @@ -78,7 +80,7 @@ * error message is returned in the 'errbuf' variable. */ int -rpcap_senderror(SOCKET sock, uint8 ver, unsigned short errcode, const char *error, char *errbuf) +rpcap_senderror(SOCKET sock, SSL *ssl, uint8 ver, unsigned short errcode, const char *error, char *errbuf) { char sendbuf[RPCAP_NETBUF_SIZE]; /* temporary buffer in which data to be sent is buffered */ int sendbufidx = 0; /* index which keeps the number of bytes currently buffered */ @@ -99,7 +101,7 @@ rpcap_senderror(SOCKET sock, uint8 ver, unsigned short errcode, const char *erro RPCAP_NETBUF_SIZE, SOCKBUF_BUFFERIZE, errbuf, PCAP_ERRBUF_SIZE)) return -1; - if (sock_send(sock, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) < 0) + if (sock_send(sock, ssl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) < 0) return -1; return 0; diff --git a/rpcap-protocol.h b/rpcap-protocol.h index 8ae8b62d13bf..a93b0a8b4328 100644 --- a/rpcap-protocol.h +++ b/rpcap-protocol.h @@ -132,10 +132,12 @@ * XXX - use the C99 types? Microsoft's newer versions of Visual Studio * support them. */ +#ifndef __HAIKU__ typedef unsigned char uint8; /* 8-bit unsigned integer */ typedef unsigned short uint16; /* 16-bit unsigned integer */ typedef unsigned int uint32; /* 32-bit unsigned integer */ typedef int int32; /* 32-bit signed integer */ +#endif /* Common header for all the RPCAP messages */ struct rpcap_header @@ -154,6 +156,25 @@ struct rpcap_header * Older servers don't provide this; they support only version 0. */ struct rpcap_authreply +{ + uint8 minvers; /* Minimum version supported */ + uint8 maxvers; /* Maximum version supported */ + uint8 pad[2]; /* Pad to 4-byte boundary **/ + uint32 byte_order_magic; /* RPCAP_BYTE_ORDER_MAGIC, in server byte order */ +}; + +/* + * Any resemblance between this and the pcap file magic number + * is purely coincidental, trust me. + */ +#define RPCAP_BYTE_ORDER_MAGIC 0xa1b2c3d4U +#define RPCAP_BYTE_ORDER_MAGIC_SWAPPED 0xd4c3b2a1U + +/* + * Older version of authentication reply, without byte order indication + * and padding. + */ +struct rpcap_authreply_old { uint8 minvers; /* Minimum version supported */ uint8 maxvers; /* Maximum version supported */ @@ -259,7 +280,7 @@ struct rpcap_findalldevs_ifaddr struct rpcap_openreply { int32 linktype; /* Link type */ - int32 tzoff; /* Timezone offset */ + int32 tzoff; /* Timezone offset - not used by newer clients */ }; /* Format of the message that starts a remote capture (startcap command) */ @@ -287,10 +308,14 @@ struct rpcap_startcapreply */ struct rpcap_pkthdr { + /* + * This protocol needs to be updated with a new version before + * 2038-01-19 03:14:07 UTC. + */ uint32 timestamp_sec; /* 'struct timeval' compatible, it represents the 'tv_sec' field */ uint32 timestamp_usec; /* 'struct timeval' compatible, it represents the 'tv_usec' field */ uint32 caplen; /* Length of portion present in the capture */ - uint32 len; /* Real length this packet (off wire) */ + uint32 len; /* Real length of this packet (off wire) */ uint32 npkt; /* Ordinal number of the packet (i.e. the first one captured has '1', the second one '2', etc) */ }; @@ -302,7 +327,7 @@ struct rpcap_filter uint32 nitems; /* Number of items contained into the filter (e.g. BPF instructions for BPF filters) */ }; -/* Structure that keeps a single BPF instuction; it is repeated 'ninsn' times according to the 'rpcap_filterbpf' header */ +/* Structure that keeps a single BPF instruction; it is repeated 'ninsn' times according to the 'rpcap_filterbpf' header */ struct rpcap_filterbpf_insn { uint16 code; /* opcode of the instruction */ @@ -346,17 +371,17 @@ struct rpcap_sampling */ #define RPCAP_MSG_IS_REPLY 0x080 /* Flag indicating a reply */ -#define RPCAP_MSG_ERROR 1 /* Message that keeps an error notification */ -#define RPCAP_MSG_FINDALLIF_REQ 2 /* Request to list all the remote interfaces */ -#define RPCAP_MSG_OPEN_REQ 3 /* Request to open a remote device */ -#define RPCAP_MSG_STARTCAP_REQ 4 /* Request to start a capture on a remote device */ -#define RPCAP_MSG_UPDATEFILTER_REQ 5 /* Send a compiled filter into the remote device */ -#define RPCAP_MSG_CLOSE 6 /* Close the connection with the remote peer */ -#define RPCAP_MSG_PACKET 7 /* This is a 'data' message, which carries a network packet */ -#define RPCAP_MSG_AUTH_REQ 8 /* Message that keeps the authentication parameters */ -#define RPCAP_MSG_STATS_REQ 9 /* It requires to have network statistics */ -#define RPCAP_MSG_ENDCAP_REQ 10 /* Stops the current capture, keeping the device open */ -#define RPCAP_MSG_SETSAMPLING_REQ 11 /* Set sampling parameters */ +#define RPCAP_MSG_ERROR 0x01 /* Message that keeps an error notification */ +#define RPCAP_MSG_FINDALLIF_REQ 0x02 /* Request to list all the remote interfaces */ +#define RPCAP_MSG_OPEN_REQ 0x03 /* Request to open a remote device */ +#define RPCAP_MSG_STARTCAP_REQ 0x04 /* Request to start a capture on a remote device */ +#define RPCAP_MSG_UPDATEFILTER_REQ 0x05 /* Send a compiled filter into the remote device */ +#define RPCAP_MSG_CLOSE 0x06 /* Close the connection with the remote peer */ +#define RPCAP_MSG_PACKET 0x07 /* This is a 'data' message, which carries a network packet */ +#define RPCAP_MSG_AUTH_REQ 0x08 /* Message that keeps the authentication parameters */ +#define RPCAP_MSG_STATS_REQ 0x09 /* It requires to have network statistics */ +#define RPCAP_MSG_ENDCAP_REQ 0x0A /* Stops the current capture, keeping the device open */ +#define RPCAP_MSG_SETSAMPLING_REQ 0x0B /* Set sampling parameters */ #define RPCAP_MSG_FINDALLIF_REPLY (RPCAP_MSG_FINDALLIF_REQ | RPCAP_MSG_IS_REPLY) /* Keeps the list of all the remote interfaces */ #define RPCAP_MSG_OPEN_REPLY (RPCAP_MSG_OPEN_REQ | RPCAP_MSG_IS_REPLY) /* The remote device has been opened correctly */ @@ -415,9 +440,10 @@ struct rpcap_sampling *********************************************************/ #include "sockutils.h" +#include "sslutils.h" extern void rpcap_createhdr(struct rpcap_header *header, uint8 ver, uint8 type, uint16 value, uint32 length); extern const char *rpcap_msg_type_string(uint8 type); -extern int rpcap_senderror(SOCKET sock, uint8 ver, uint16 errcode, const char *error, char *errbuf); +extern int rpcap_senderror(SOCKET sock, SSL *ssl, uint8 ver, uint16 errcode, const char *error, char *errbuf); #endif diff --git a/rpcapd/CMakeLists.txt b/rpcapd/CMakeLists.txt index 1821c8566ed4..8b30be3c7c4c 100644 --- a/rpcapd/CMakeLists.txt +++ b/rpcapd/CMakeLists.txt @@ -50,8 +50,8 @@ if(WIN32 OR ((CMAKE_USE_PTHREADS_INIT OR PTHREADS_FOUND) AND HAVE_CRYPT)) if(WIN32) set(RPCAPD_EXTRA_SOURCES win32-svc.c + ${pcap_SOURCE_DIR}/charconv.c ${pcap_SOURCE_DIR}/missing/getopt.c - ${pcap_SOURCE_DIR}/missing/win_snprintf.c rpcapd.rc) include_directories(${pcap_SOURCE_DIR}/rpcapd ${pcap_SOURCE_DIR}/missing) endif(WIN32) @@ -63,6 +63,7 @@ if(WIN32 OR ((CMAKE_USE_PTHREADS_INIT OR PTHREADS_FOUND) AND HAVE_CRYPT)) rpcapd.c ${pcap_SOURCE_DIR}/rpcap-protocol.c ${pcap_SOURCE_DIR}/sockutils.c + ${pcap_SOURCE_DIR}/sslutils.c ${pcap_SOURCE_DIR}/fmtutils.c ${RPCAPD_EXTRA_SOURCES} ) @@ -71,6 +72,11 @@ if(WIN32 OR ((CMAKE_USE_PTHREADS_INIT OR PTHREADS_FOUND) AND HAVE_CRYPT)) set_target_properties(rpcapd PROPERTIES COMPILE_FLAGS ${C_ADDITIONAL_FLAGS}) endif() + if(NOT "${SANITIZER_FLAGS}" STREQUAL "") + set_target_properties(rpcapd PROPERTIES + LINK_FLAGS "${SANITIZER_FLAGS}") + endif() + # # By default, build rpcapd universal with the appropriate set of # architectures for the OS on which we're doing the build. @@ -129,11 +135,32 @@ if(WIN32 OR ((CMAKE_USE_PTHREADS_INIT OR PTHREADS_FOUND) AND HAVE_CRYPT)) set(MANFILE_EXPAND rpcapd-config.manfile.in) - if(MSVC AND CMAKE_SIZEOF_VOID_P EQUAL 8) - install(TARGETS rpcapd DESTINATION bin/amd64) - else(MSVC AND CMAKE_SIZEOF_VOID_P EQUAL 8) - install(TARGETS rpcapd DESTINATION bin) - endif(MSVC AND CMAKE_SIZEOF_VOID_P EQUAL 8) + if(WIN32) + # + # XXX - where should the install target put rpcapd on Windows? + # + # Note that if an installer package is being produced + # from the results of the build, the installer package + # will determine where it goes. + # + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + install(TARGETS rpcapd DESTINATION bin/amd64) + else(CMAKE_SIZEOF_VOID_P EQUAL 8) + install(TARGETS rpcapd DESTINATION bin) + endif(CMAKE_SIZEOF_VOID_P EQUAL 8) + else(WIN32) + # + # On UN*X, we put it in the sbin directory. + # + # XXX - the Linux Filesystem Hierarchy Standard says /usr/sbin + # is for daemons, but some other systems use /usr/libexec instead. + # However, since some users might, instead of having rpcapd be + # launched by inetd/xinetd/launchd/systemd, just run it on a + # machine when remote capture is to be done, a case can be made + # for the sbin directory even on systems with /usr/libexec. + # + install(TARGETS rpcapd DESTINATION ${CMAKE_INSTALL_SBINDIR}) + endif(WIN32) # On UN*X, and on Windows when not using MSVC, generate process man # pages and arrange that they be installed. diff --git a/rpcapd/Makefile.in b/rpcapd/Makefile.in index 88e632a26250..32906790fba1 100644 --- a/rpcapd/Makefile.in +++ b/rpcapd/Makefile.in @@ -38,6 +38,7 @@ mandir = @mandir@ # VPATH srcdir = @srcdir@ +top_srcdir = @top_srcdir@ VPATH = @srcdir@ # @@ -84,7 +85,7 @@ SRC = daemon.c \ log.c \ rpcapd.c -OBJ = $(SRC:.c=.o) ../rpcap-protocol.o ../sockutils.o ../fmtutils.o +OBJ = $(SRC:.c=.o) ../rpcap-protocol.o ../sockutils.o ../fmtutils.o ../sslutils.o PUBHDR = HDR = $(PUBHDR) log.h @@ -138,4 +139,4 @@ tags: $(TAGFILES) ctags -wtd $(TAGFILES) depend: - ../$(MKDEP) -c "$(CC)" -m "$(DEPENDENCY_CFLAG)" $(CFLAGS) $(DEFS) $(INCLS) $(SRC) + $(MKDEP) -c "$(CC)" -m "$(DEPENDENCY_CFLAG)" -s "$(srcdir)" $(CFLAGS) $(DEFS) $(INCLS) $(SRC) diff --git a/rpcapd/daemon.c b/rpcapd/daemon.c index 209dba225ea6..9b0f82851526 100644 --- a/rpcapd/daemon.c +++ b/rpcapd/daemon.c @@ -39,6 +39,7 @@ #include // for the errno variable #include // for malloc(), free(), ... #include // for strlen(), ... +#include // for INT_MAX #ifdef _WIN32 #include // for threads @@ -64,6 +65,11 @@ #include "daemon.h" #include "log.h" +#ifdef HAVE_OPENSSL +#include +#include "sslutils.h" +#endif + // // Timeout, in seconds, when we're waiting for a client to send us an // authentication request; if they don't send us a request within that @@ -90,6 +96,7 @@ struct daemon_slpars { SOCKET sockctrl; //!< SOCKET ID of the control connection + SSL *ssl; //!< Optional SSL handler for the controlling sockets int isactive; //!< Not null if the daemon has to run in active mode int nullAuthAllowed; //!< '1' if we permit NULL authentication, '0' otherwise }; @@ -105,6 +112,7 @@ struct daemon_slpars struct session { SOCKET sockctrl; SOCKET sockdata; + SSL *ctrl_ssl, *data_ssl; // optional SSL handlers for sockctrl and sockdata. uint8 protocol_version; pcap_t *fp; unsigned int TotCapt; @@ -117,7 +125,7 @@ struct session { }; // Locally defined functions -static int daemon_msg_err(SOCKET sockctrl, uint32 plen); +static int daemon_msg_err(SOCKET sockctrl, SSL *, uint32 plen); static int daemon_msg_auth_req(struct daemon_slpars *pars, uint32 plen); static int daemon_AuthUserPwd(char *username, char *password, char *errbuf); @@ -128,13 +136,13 @@ static int daemon_msg_open_req(uint8 ver, struct daemon_slpars *pars, uint32 plen, char *source, size_t sourcelen); static int daemon_msg_startcap_req(uint8 ver, struct daemon_slpars *pars, uint32 plen, char *source, struct session **sessionp, - struct rpcap_sampling *samp_param); + struct rpcap_sampling *samp_param, int uses_ssl); static int daemon_msg_endcap_req(uint8 ver, struct daemon_slpars *pars, struct session *session); static int daemon_msg_updatefilter_req(uint8 ver, struct daemon_slpars *pars, struct session *session, uint32 plen); -static int daemon_unpackapplyfilter(SOCKET sockctrl, struct session *session, uint32 *plenp, char *errbuf); +static int daemon_unpackapplyfilter(SOCKET sockctrl, SSL *, struct session *session, uint32 *plenp, char *errbuf); static int daemon_msg_stats_req(uint8 ver, struct daemon_slpars *pars, struct session *session, uint32 plen, struct pcap_stat *stats, @@ -151,21 +159,69 @@ static void *daemon_thrdatamain(void *ptr); static void noop_handler(int sign); #endif -static int rpcapd_recv_msg_header(SOCKET sock, struct rpcap_header *headerp); -static int rpcapd_recv(SOCKET sock, char *buffer, size_t toread, uint32 *plen, char *errmsgbuf); -static int rpcapd_discard(SOCKET sock, uint32 len); +static int rpcapd_recv_msg_header(SOCKET sock, SSL *, struct rpcap_header *headerp); +static int rpcapd_recv(SOCKET sock, SSL *, char *buffer, size_t toread, uint32 *plen, char *errmsgbuf); +static int rpcapd_discard(SOCKET sock, SSL *, uint32 len); static void session_close(struct session *); +// +// TLS record layer header; used when processing the first message from +// the client, in case we aren't doing TLS but they are. +// +struct tls_record_header { + uint8 type; // ContentType - will be 22, for Handshake + uint8 version_major; // TLS protocol major version + uint8 version_injor; // TLS protocol minor version + // This is *not* aligned on a 2-byte boundary; we just + // declare it as two bytes. Don't assume any particular + // compiler's mechanism for saying "packed"! + uint8 length_hi; // Upper 8 bits of payload length + uint8 length_lo; // Low 8 bits of payload length +}; + +#define TLS_RECORD_HEADER_LEN 5 // Don't use sizeof in case it's padded + +#define TLS_RECORD_TYPE_ALERT 21 +#define TLS_RECORD_TYPE_HANDSHAKE 22 + +// +// TLS alert message. +// +struct tls_alert { + uint8 alert_level; + uint8 alert_description; +}; + +#define TLS_ALERT_LEN 2 + +#define TLS_ALERT_LEVEL_FATAL 2 +#define TLS_ALERT_HANDSHAKE_FAILURE 40 + static int is_url(const char *source); +/* + * Maximum sizes for fixed-bit-width values. + */ +#ifndef UINT16_MAX +#define UINT16_MAX 65535U +#endif + +#ifndef UINT32_MAX +#define UINT32_MAX 4294967295U +#endif + int daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, - int nullAuthAllowed) + int nullAuthAllowed, int uses_ssl) { + uint8 first_octet; + struct tls_record_header tls_header; + struct tls_alert tls_alert; struct daemon_slpars pars; // service loop parameters char errbuf[PCAP_ERRBUF_SIZE + 1]; // keeps the error string, prior to be printed char errmsgbuf[PCAP_ERRBUF_SIZE + 1]; // buffer for errors to send to the client int host_port_check_status; + SSL *ssl = NULL; int nrecv; struct rpcap_header header; // RPCAP message general header uint32 plen; // payload length from header @@ -192,8 +248,177 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, *errbuf = 0; // Initialize errbuf + // + // Peek into the socket to determine whether the client sent us + // a TLS handshake message or a non-TLS rpcapd message. + // + // The first byte of an rpcapd request is the version number; + // the first byte of a TLS handshake message is 22. The + // first request to an rpcapd server must be an authentication + // request or a close request, and must have a version number + // of 0, so it will be possible to distinguish between an + // initial plaintext request to a server and an initial TLS + // handshake message. + // + nrecv = sock_recv(sockctrl, NULL, (char *)&first_octet, 1, + SOCK_EOF_ISNT_ERROR|SOCK_MSG_PEEK, errbuf, PCAP_ERRBUF_SIZE); + if (nrecv == -1) + { + // Fatal error. + rpcapd_log(LOGPRIO_ERROR, "Peek from client failed: %s", errbuf); + goto end; + } + if (nrecv == 0) + { + // Client closed the connection. + goto end; + } + +#ifdef HAVE_OPENSSL + // + // We have to upgrade to TLS as soon as possible, so that the + // whole protocol goes through the encrypted tunnel, including + // early error messages. + // + // Even in active mode, the other end has to initiate the TLS + // handshake as we still are the server as far as TLS is concerned, + // so we don't check isactive. + // + if (uses_ssl) + { + // + // We're expecting a TLS handshake message. If this + // isn't one, assume it's a non-TLS rpcapd message. + // + // The first octet of a TLS handshake is + // TLS_RECORD_TYPE_HANDSHAKE. + // + if (first_octet != TLS_RECORD_TYPE_HANDSHAKE) + { + // + // We assume this is a non-TLS rpcapd message. + // + // Read the message header from the client. + // + nrecv = rpcapd_recv_msg_header(sockctrl, NULL, &header); + if (nrecv == -1) + { + // Fatal error. + goto end; + } + if (nrecv == -2) + { + // Client closed the connection. + goto end; + } + plen = header.plen; + + // Discard the rest of the message. + if (rpcapd_discard(sockctrl, NULL, plen) == -1) + { + // Network error. + goto end; + } + + // + // Send an authentication error, indicating + // that we require TLS. + // + if (rpcap_senderror(sockctrl, NULL, header.ver, + PCAP_ERR_TLS_REQUIRED, + "TLS is required by this server", errbuf) == -1) + { + // That failed; log a message and give up. + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + goto end; + } + + // Shut the session down. + goto end; + } + ssl = ssl_promotion(1, sockctrl, errbuf, PCAP_ERRBUF_SIZE); + if (! ssl) + { + rpcapd_log(LOGPRIO_ERROR, "TLS handshake on control connection failed: %s", + errbuf); + goto end; + } + } + else +#endif + { + // + // We're expecting a non-TLS rpcapd message. If this + // looks, instead, like a TLS handshake message, send + // a TLS handshake_failed alert. + // + // The first octet of a TLS handshake is + // TLS_RECORD_TYPE_HANDSHAKE. + // + if (first_octet == TLS_RECORD_TYPE_HANDSHAKE) + { + // + // TLS handshake. + // Read the record header. + // + nrecv = sock_recv(sockctrl, ssl, (char *) &tls_header, + sizeof tls_header, SOCK_RECEIVEALL_YES|SOCK_EOF_ISNT_ERROR, + errbuf, PCAP_ERRBUF_SIZE); + if (nrecv == -1) + { + // Network error. + rpcapd_log(LOGPRIO_ERROR, "Read from client failed: %s", errbuf); + goto end; + } + if (nrecv == 0) + { + // Immediate EOF + goto end; + } + plen = (tls_header.length_hi << 8U) | tls_header.length_lo; + + // Discard the rest of the message. + if (rpcapd_discard(sockctrl, NULL, plen) == -1) + { + // Network error. + goto end; + } + + // + // Send a TLS handshake failure alert. + // Use the same version the client sent us. + // + tls_header.type = TLS_RECORD_TYPE_ALERT; + tls_header.length_hi = 0; + tls_header.length_lo = TLS_ALERT_LEN; + + if (sock_send(sockctrl, NULL, (char *) &tls_header, + TLS_RECORD_HEADER_LEN, errbuf, PCAP_ERRBUF_SIZE) == -1) + { + // That failed; log a message and give up. + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + goto end; + } + + tls_alert.alert_level = TLS_ALERT_LEVEL_FATAL; + tls_alert.alert_description = TLS_ALERT_HANDSHAKE_FAILURE; + if (sock_send(sockctrl, NULL, (char *) &tls_alert, + TLS_ALERT_LEN, errbuf, PCAP_ERRBUF_SIZE) == -1) + { + // That failed; log a message and give up. + rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); + goto end; + } + // + // Give up anyway. + // + goto end; + } + } + // Set parameters structure pars.sockctrl = sockctrl; + pars.ssl = ssl; pars.isactive = isactive; // active mode pars.nullAuthAllowed = nullAuthAllowed; @@ -224,8 +449,9 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, if (getpeername(pars.sockctrl, (struct sockaddr *)&from, &fromlen) == -1) { - sock_geterror("getpeername()", errmsgbuf, PCAP_ERRBUF_SIZE); - if (rpcap_senderror(pars.sockctrl, 0, PCAP_ERR_NETW, errmsgbuf, errbuf) == -1) + sock_geterrmsg(errmsgbuf, PCAP_ERRBUF_SIZE, + "getpeername() failed"); + if (rpcap_senderror(pars.sockctrl, pars.ssl, 0, PCAP_ERR_NETW, errmsgbuf, errbuf) == -1) rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); goto end; } @@ -248,7 +474,7 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, // // Sorry, we can't let you in. // - if (rpcap_senderror(pars.sockctrl, 0, PCAP_ERR_HOSTNOAUTH, errmsgbuf, errbuf) == -1) + if (rpcap_senderror(pars.sockctrl, pars.ssl, 0, PCAP_ERR_HOSTNOAUTH, errmsgbuf, errbuf) == -1) rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); goto end; } @@ -295,11 +521,12 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, FD_SET(pars.sockctrl, &rfds); - retval = select(pars.sockctrl + 1, &rfds, NULL, NULL, &tv); + retval = select((int)pars.sockctrl + 1, &rfds, NULL, NULL, &tv); if (retval == -1) { - sock_geterror("select() failed", errmsgbuf, PCAP_ERRBUF_SIZE); - if (rpcap_senderror(pars.sockctrl, 0, PCAP_ERR_NETW, errmsgbuf, errbuf) == -1) + sock_geterrmsg(errmsgbuf, PCAP_ERRBUF_SIZE, + "select() failed"); + if (rpcap_senderror(pars.sockctrl, pars.ssl, 0, PCAP_ERR_NETW, errmsgbuf, errbuf) == -1) rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); goto end; } @@ -308,7 +535,7 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, // So, this was a fake connection. Drop it down if (retval == 0) { - if (rpcap_senderror(pars.sockctrl, 0, PCAP_ERR_INITTIMEOUT, "The RPCAP initial timeout has expired", errbuf) == -1) + if (rpcap_senderror(pars.sockctrl, pars.ssl, 0, PCAP_ERR_INITTIMEOUT, "The RPCAP initial timeout has expired", errbuf) == -1) rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); goto end; } @@ -317,7 +544,7 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, // // Read the message header from the client. // - nrecv = rpcapd_recv_msg_header(pars.sockctrl, &header); + nrecv = rpcapd_recv_msg_header(pars.sockctrl, pars.ssl, &header); if (nrecv == -1) { // Fatal error. @@ -340,7 +567,7 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, // // Send it back to them with their version. // - if (rpcap_senderror(pars.sockctrl, header.ver, + if (rpcap_senderror(pars.sockctrl, pars.ssl, header.ver, PCAP_ERR_WRONGVER, "RPCAP version in requests in the authentication phase must be 0", errbuf) == -1) @@ -352,7 +579,7 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, // Discard the rest of the message and drop the // connection. - (void)rpcapd_discard(pars.sockctrl, plen); + (void)rpcapd_discard(pars.sockctrl, pars.ssl, plen); goto end; } @@ -385,7 +612,7 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, // Discard the rest of the message, if // there is anything more. // - (void)rpcapd_discard(pars.sockctrl, plen); + (void)rpcapd_discard(pars.sockctrl, pars.ssl, plen); // We're done with this client. goto end; @@ -397,7 +624,7 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, // an error message rather than a "let // me log in" message, indicating that // we're not allowed to connect to them? - (void)daemon_msg_err(pars.sockctrl, plen); + (void)daemon_msg_err(pars.sockctrl, pars.ssl, plen); goto end; case RPCAP_MSG_FINDALLIF_REQ: @@ -414,20 +641,21 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, msg_type_string = rpcap_msg_type_string(header.type); if (msg_type_string != NULL) { - pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "%s request sent before authentication was completed", msg_type_string); + snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "%s request sent before authentication was completed", msg_type_string); } else { - pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Message of type %u sent before authentication was completed", header.type); + snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Message of type %u sent before authentication was completed", header.type); } - if (rpcap_senderror(pars.sockctrl, header.ver, - PCAP_ERR_WRONGMSG, errmsgbuf, errbuf) == -1) + if (rpcap_senderror(pars.sockctrl, pars.ssl, + header.ver, PCAP_ERR_WRONGMSG, + errmsgbuf, errbuf) == -1) { rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); goto end; } // Discard the rest of the message. - if (rpcapd_discard(pars.sockctrl, plen) == -1) + if (rpcapd_discard(pars.sockctrl, pars.ssl, plen) == -1) { // Network error. goto end; @@ -449,20 +677,21 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, msg_type_string = rpcap_msg_type_string(header.type); if (msg_type_string != NULL) { - pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Server-to-client message %s received from client", msg_type_string); + snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Server-to-client message %s received from client", msg_type_string); } else { - pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Server-to-client message of type %u received from client", header.type); + snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Server-to-client message of type %u received from client", header.type); } - if (rpcap_senderror(pars.sockctrl, header.ver, - PCAP_ERR_WRONGMSG, errmsgbuf, errbuf) == -1) + if (rpcap_senderror(pars.sockctrl, pars.ssl, + header.ver, PCAP_ERR_WRONGMSG, + errmsgbuf, errbuf) == -1) { rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); goto end; } // Discard the rest of the message. - if (rpcapd_discard(pars.sockctrl, plen) == -1) + if (rpcapd_discard(pars.sockctrl, pars.ssl, plen) == -1) { // Fatal error. goto end; @@ -473,15 +702,16 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, // // Unknown message type. // - pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Unknown message type %u", header.type); - if (rpcap_senderror(pars.sockctrl, header.ver, - PCAP_ERR_WRONGMSG, errmsgbuf, errbuf) == -1) + snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Unknown message type %u", header.type); + if (rpcap_senderror(pars.sockctrl, pars.ssl, + header.ver, PCAP_ERR_WRONGMSG, + errmsgbuf, errbuf) == -1) { rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); goto end; } // Discard the rest of the message. - if (rpcapd_discard(pars.sockctrl, plen) == -1) + if (rpcapd_discard(pars.sockctrl, pars.ssl, plen) == -1) { // Fatal error. goto end; @@ -519,7 +749,7 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, // // Be carefully: the capture can have been started, but an error occurred (so session != NULL, but // sockdata is 0 - if ((!pars.isactive) && ((session == NULL) || ((session != NULL) && (session->sockdata == 0)))) + if ((!pars.isactive) && (session == NULL || session->sockdata == 0)) { // Check for the initial timeout FD_ZERO(&rfds); @@ -528,13 +758,18 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, tv.tv_usec = 0; FD_SET(pars.sockctrl, &rfds); - - retval = select(pars.sockctrl + 1, &rfds, NULL, NULL, &tv); +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + retval = 1; +#else + retval = select((int)pars.sockctrl + 1, &rfds, NULL, NULL, &tv); +#endif if (retval == -1) { - sock_geterror("select() failed", errmsgbuf, PCAP_ERRBUF_SIZE); - if (rpcap_senderror(pars.sockctrl, 0, - PCAP_ERR_NETW, errmsgbuf, errbuf) == -1) + sock_geterrmsg(errmsgbuf, PCAP_ERRBUF_SIZE, + "select() failed"); + if (rpcap_senderror(pars.sockctrl, pars.ssl, + 0, PCAP_ERR_NETW, + errmsgbuf, errbuf) == -1) rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); goto end; } @@ -543,8 +778,8 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, // So, this was a fake connection. Drop it down if (retval == 0) { - if (rpcap_senderror(pars.sockctrl, 0, - PCAP_ERR_INITTIMEOUT, + if (rpcap_senderror(pars.sockctrl, pars.ssl, + 0, PCAP_ERR_INITTIMEOUT, "The RPCAP initial timeout has expired", errbuf) == -1) rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); @@ -555,7 +790,7 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, // // Read the message header from the client. // - nrecv = rpcapd_recv_msg_header(pars.sockctrl, &header); + nrecv = rpcapd_recv_msg_header(pars.sockctrl, pars.ssl, &header); if (nrecv == -1) { // Fatal error. @@ -581,7 +816,7 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, // so they don't reject it as having the wrong // version. // - if (rpcap_senderror(pars.sockctrl, + if (rpcap_senderror(pars.sockctrl, pars.ssl, header.ver, PCAP_ERR_WRONGVER, "RPCAP version in message isn't supported by the server", errbuf) == -1) @@ -592,7 +827,7 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, } // Discard the rest of the message. - (void)rpcapd_discard(pars.sockctrl, plen); + (void)rpcapd_discard(pars.sockctrl, pars.ssl, plen); // Give up on them. goto end; } @@ -601,7 +836,7 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, { case RPCAP_MSG_ERROR: // The other endpoint reported an error { - (void)daemon_msg_err(pars.sockctrl, plen); + (void)daemon_msg_err(pars.sockctrl, pars.ssl, plen); // Do nothing; just exit; the error code is already into the errbuf // XXX - actually exit.... break; @@ -647,7 +882,7 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, { // They never told us what device // to capture on! - if (rpcap_senderror(pars.sockctrl, + if (rpcap_senderror(pars.sockctrl, pars.ssl, header.ver, PCAP_ERR_STARTCAPTURE, "No capture device was specified", @@ -658,7 +893,7 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); goto end; } - if (rpcapd_discard(pars.sockctrl, plen) == -1) + if (rpcapd_discard(pars.sockctrl, pars.ssl, plen) == -1) { goto end; } @@ -666,7 +901,8 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, } if (daemon_msg_startcap_req(header.ver, &pars, - plen, source, &session, &samp_param) == -1) + plen, source, &session, &samp_param, + uses_ssl) == -1) { // Fatal error; a message has // been logged, so just give up. @@ -689,8 +925,9 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, } else { - if (rpcap_senderror(pars.sockctrl, - header.ver, PCAP_ERR_UPDATEFILTER, + if (rpcap_senderror(pars.sockctrl, pars.ssl, + header.ver, + PCAP_ERR_UPDATEFILTER, "Device not opened. Cannot update filter", errbuf) == -1) { @@ -757,8 +994,9 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, } else { - rpcap_senderror(pars.sockctrl, - header.ver, PCAP_ERR_ENDCAPTURE, + rpcap_senderror(pars.sockctrl, pars.ssl, + header.ver, + PCAP_ERR_ENDCAPTURE, "Device not opened. Cannot close the capture", errbuf); } @@ -784,7 +1022,8 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, // get to reauthenticate. // rpcapd_log(LOGPRIO_INFO, "The client sent an RPCAP_MSG_AUTH_REQ message after authentication was completed"); - if (rpcap_senderror(pars.sockctrl, header.ver, + if (rpcap_senderror(pars.sockctrl, pars.ssl, + header.ver, PCAP_ERR_WRONGMSG, "RPCAP_MSG_AUTH_REQ request sent after authentication was completed", errbuf) == -1) @@ -793,7 +1032,7 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, goto end; } // Discard the rest of the message. - if (rpcapd_discard(pars.sockctrl, plen) == -1) + if (rpcapd_discard(pars.sockctrl, pars.ssl, plen) == -1) { // Fatal error. goto end; @@ -816,21 +1055,22 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, if (msg_type_string != NULL) { rpcapd_log(LOGPRIO_INFO, "The client sent a %s server-to-client message", msg_type_string); - pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Server-to-client message %s received from client", msg_type_string); + snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Server-to-client message %s received from client", msg_type_string); } else { rpcapd_log(LOGPRIO_INFO, "The client sent a server-to-client message of type %u", header.type); - pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Server-to-client message of type %u received from client", header.type); + snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Server-to-client message of type %u received from client", header.type); } - if (rpcap_senderror(pars.sockctrl, header.ver, - PCAP_ERR_WRONGMSG, errmsgbuf, errbuf) == -1) + if (rpcap_senderror(pars.sockctrl, pars.ssl, + header.ver, PCAP_ERR_WRONGMSG, + errmsgbuf, errbuf) == -1) { rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); goto end; } // Discard the rest of the message. - if (rpcapd_discard(pars.sockctrl, plen) == -1) + if (rpcapd_discard(pars.sockctrl, pars.ssl, plen) == -1) { // Fatal error. goto end; @@ -842,15 +1082,16 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, // Unknown message type. // rpcapd_log(LOGPRIO_INFO, "The client sent a message of type %u", header.type); - pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Unknown message type %u", header.type); - if (rpcap_senderror(pars.sockctrl, header.ver, - PCAP_ERR_WRONGMSG, errbuf, errmsgbuf) == -1) + snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Unknown message type %u", header.type); + if (rpcap_senderror(pars.sockctrl, pars.ssl, + header.ver, PCAP_ERR_WRONGMSG, + errbuf, errmsgbuf) == -1) { rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); goto end; } // Discard the rest of the message. - if (rpcapd_discard(pars.sockctrl, plen) == -1) + if (rpcapd_discard(pars.sockctrl, pars.ssl, plen) == -1) { // Fatal error. goto end; @@ -870,6 +1111,21 @@ end: session = NULL; } + if (passiveClients) { + free(passiveClients); + } + // + // Finish using the SSL handle for the control socket, if we + // have an SSL connection, and close the control socket. + // +#ifdef HAVE_OPENSSL + if (ssl) + { + // Finish using the SSL handle for the socket. + // This must be done *before* the socket is closed. + ssl_finish(ssl); + } +#endif sock_close(sockctrl, NULL, 0); // Print message and return @@ -882,7 +1138,7 @@ end: * This handles the RPCAP_MSG_ERR message. */ static int -daemon_msg_err(SOCKET sockctrl, uint32 plen) +daemon_msg_err(SOCKET sockctrl, SSL *ssl, uint32 plen) { char errbuf[PCAP_ERRBUF_SIZE]; char remote_errbuf[PCAP_ERRBUF_SIZE]; @@ -893,7 +1149,7 @@ daemon_msg_err(SOCKET sockctrl, uint32 plen) * Message is too long; just read as much of it as we * can into the buffer provided, and discard the rest. */ - if (sock_recv(sockctrl, remote_errbuf, PCAP_ERRBUF_SIZE - 1, + if (sock_recv(sockctrl, ssl, remote_errbuf, PCAP_ERRBUF_SIZE - 1, SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf, PCAP_ERRBUF_SIZE) == -1) { @@ -901,7 +1157,7 @@ daemon_msg_err(SOCKET sockctrl, uint32 plen) rpcapd_log(LOGPRIO_ERROR, "Read from client failed: %s", errbuf); return -1; } - if (rpcapd_discard(sockctrl, plen - (PCAP_ERRBUF_SIZE - 1)) == -1) + if (rpcapd_discard(sockctrl, ssl, plen - (PCAP_ERRBUF_SIZE - 1)) == -1) { // Network error. return -1; @@ -919,7 +1175,7 @@ daemon_msg_err(SOCKET sockctrl, uint32 plen) } else { - if (sock_recv(sockctrl, remote_errbuf, plen, + if (sock_recv(sockctrl, ssl, remote_errbuf, plen, SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf, PCAP_ERRBUF_SIZE) == -1) { @@ -971,7 +1227,7 @@ daemon_msg_auth_req(struct daemon_slpars *pars, uint32 plen) int sendbufidx = 0; // index which keeps the number of bytes currently buffered struct rpcap_authreply *authreply; // authentication reply message - status = rpcapd_recv(pars->sockctrl, (char *) &auth, sizeof(struct rpcap_auth), &plen, errmsgbuf); + status = rpcapd_recv(pars->sockctrl, pars->ssl, (char *) &auth, sizeof(struct rpcap_auth), &plen, errmsgbuf); if (status == -1) { return -1; @@ -988,10 +1244,10 @@ daemon_msg_auth_req(struct daemon_slpars *pars, uint32 plen) if (!pars->nullAuthAllowed) { // Send the client an error reply. - pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, + snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Authentication failed; NULL authentication not permitted."); - if (rpcap_senderror(pars->sockctrl, 0, - PCAP_ERR_AUTH_FAILED, errmsgbuf, errbuf) == -1) + if (rpcap_senderror(pars->sockctrl, pars->ssl, + 0, PCAP_ERR_AUTH_FAILED, errmsgbuf, errbuf) == -1) { // That failed; log a message and give up. rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); @@ -1015,7 +1271,7 @@ daemon_msg_auth_req(struct daemon_slpars *pars, uint32 plen) PCAP_ERRBUF_SIZE, errno, "malloc() failed"); goto error; } - status = rpcapd_recv(pars->sockctrl, username, usernamelen, &plen, errmsgbuf); + status = rpcapd_recv(pars->sockctrl, pars->ssl, username, usernamelen, &plen, errmsgbuf); if (status == -1) { free(username); @@ -1037,7 +1293,7 @@ daemon_msg_auth_req(struct daemon_slpars *pars, uint32 plen) free(username); goto error; } - status = rpcapd_recv(pars->sockctrl, passwd, passwdlen, &plen, errmsgbuf); + status = rpcapd_recv(pars->sockctrl, pars->ssl, passwd, passwdlen, &plen, errmsgbuf); if (status == -1) { free(username); @@ -1060,8 +1316,8 @@ daemon_msg_auth_req(struct daemon_slpars *pars, uint32 plen) // free(username); free(passwd); - if (rpcap_senderror(pars->sockctrl, 0, - PCAP_ERR_AUTH_FAILED, errmsgbuf, errbuf) == -1) + if (rpcap_senderror(pars->sockctrl, pars->ssl, + 0, PCAP_ERR_AUTH_FAILED, errmsgbuf, errbuf) == -1) { // That failed; log a message and give up. rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); @@ -1088,10 +1344,10 @@ daemon_msg_auth_req(struct daemon_slpars *pars, uint32 plen) } default: - pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, + snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Authentication type not recognized."); - if (rpcap_senderror(pars->sockctrl, 0, - PCAP_ERR_AUTH_TYPE_NOTSUP, errmsgbuf, errbuf) == -1) + if (rpcap_senderror(pars->sockctrl, pars->ssl, + 0, PCAP_ERR_AUTH_TYPE_NOTSUP, errmsgbuf, errbuf) == -1) { // That failed; log a message and give up. rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); @@ -1115,22 +1371,27 @@ daemon_msg_auth_req(struct daemon_slpars *pars, uint32 plen) goto error; // - // Indicate to our peer what versions we support. + // Indicate to our peer what versions we support and what our + // version of the byte-order magic is (which will tell the + // client whether our byte order differs from theirs, in which + // case they will need to byte-swap some fields in some + // link-layer types' headers). // memset(authreply, 0, sizeof(struct rpcap_authreply)); authreply->minvers = RPCAP_MIN_VERSION; authreply->maxvers = RPCAP_MAX_VERSION; + authreply->byte_order_magic = RPCAP_BYTE_ORDER_MAGIC; // Send the reply. - if (sock_send(pars->sockctrl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) + if (sock_send(pars->sockctrl, pars->ssl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) { - // That failed; log a messsage and give up. + // That failed; log a message and give up. rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); return -1; } // Check if all the data has been read; if not, discard the data in excess - if (rpcapd_discard(pars->sockctrl, plen) == -1) + if (rpcapd_discard(pars->sockctrl, pars->ssl, plen) == -1) { return -1; } @@ -1138,8 +1399,8 @@ daemon_msg_auth_req(struct daemon_slpars *pars, uint32 plen) return 0; error: - if (rpcap_senderror(pars->sockctrl, 0, PCAP_ERR_AUTH, errmsgbuf, - errbuf) == -1) + if (rpcap_senderror(pars->sockctrl, pars->ssl, 0, PCAP_ERR_AUTH, + errmsgbuf, errbuf) == -1) { // That failed; log a message and give up. rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); @@ -1148,7 +1409,7 @@ error: error_noreply: // Check if all the data has been read; if not, discard the data in excess - if (rpcapd_discard(pars->sockctrl, plen) == -1) + if (rpcapd_discard(pars->sockctrl, pars->ssl, plen) == -1) { return -1; } @@ -1184,7 +1445,7 @@ daemon_AuthUserPwd(char *username, char *password, char *errbuf) if (LogonUser(username, ".", password, LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, &Token) == 0) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Authentication failed"); + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Authentication failed"); error = GetLastError(); if (error != ERROR_LOGON_FAILURE) { @@ -1201,7 +1462,7 @@ daemon_AuthUserPwd(char *username, char *password, char *errbuf) // I didn't test it. if (ImpersonateLoggedOnUser(Token) == 0) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Authentication failed"); + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Authentication failed"); pcap_fmt_errmsg_for_win32_err(errmsgbuf, PCAP_ERRBUF_SIZE, GetLastError(), "ImpersonateLoggedOnUser() failed"); rpcapd_log(LOGPRIO_ERROR, "%s", errmsgbuf); @@ -1216,7 +1477,7 @@ daemon_AuthUserPwd(char *username, char *password, char *errbuf) /* * See * - * http://www.unixpapa.com/incnote/passwd.html + * https://www.unixpapa.com/incnote/passwd.html * * We use the Solaris/Linux shadow password authentication if * we have getspnam(), otherwise we just do traditional @@ -1243,7 +1504,7 @@ daemon_AuthUserPwd(char *username, char *password, char *errbuf) // This call is needed to get the uid if ((user = getpwnam(username)) == NULL) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Authentication failed"); + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Authentication failed"); return -1; } @@ -1251,7 +1512,7 @@ daemon_AuthUserPwd(char *username, char *password, char *errbuf) // This call is needed to get the password; otherwise 'x' is returned if ((usersp = getspnam(username)) == NULL) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Authentication failed"); + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Authentication failed"); return -1; } user_password = usersp->sp_pwdp; @@ -1279,7 +1540,7 @@ daemon_AuthUserPwd(char *username, char *password, char *errbuf) if (crypt_password == NULL) { error = errno; - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Authentication failed"); + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Authentication failed"); if (error == 0) { // It didn't set errno. @@ -1294,7 +1555,7 @@ daemon_AuthUserPwd(char *username, char *password, char *errbuf) } if (strcmp(user_password, crypt_password) != 0) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Authentication failed"); + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Authentication failed"); return -1; } @@ -1324,6 +1585,21 @@ daemon_AuthUserPwd(char *username, char *password, char *errbuf) } +/* + * Make sure that the reply length won't overflow 32 bits if we add the + * specified amount to it. If it won't, add that amount to it. + * + * We check whether replylen + itemlen > UINT32_MAX, but subtract itemlen + * from both sides, to prevent overflow. + */ +#define CHECK_AND_INCREASE_REPLY_LEN(itemlen) \ + if (replylen > UINT32_MAX - (itemlen)) { \ + pcap_strlcpy(errmsgbuf, "Reply length doesn't fit in 32 bits", \ + sizeof (errmsgbuf)); \ + goto error; \ + } \ + replylen += (uint32)(itemlen) + static int daemon_msg_findallif_req(uint8 ver, struct daemon_slpars *pars, uint32 plen) { @@ -1334,12 +1610,11 @@ daemon_msg_findallif_req(uint8 ver, struct daemon_slpars *pars, uint32 plen) pcap_if_t *alldevs = NULL; // pointer to the header of the interface chain pcap_if_t *d; // temp pointer needed to scan the interface chain struct pcap_addr *address; // pcap structure that keeps a network address of an interface - struct rpcap_findalldevs_if *findalldevs_if;// rpcap structure that packet all the data of an interface together uint32 replylen; // length of reply payload uint16 nif = 0; // counts the number of interface listed // Discard the rest of the message; there shouldn't be any payload. - if (rpcapd_discard(pars->sockctrl, plen) == -1) + if (rpcapd_discard(pars->sockctrl, pars->ssl, plen) == -1) { // Network error. return -1; @@ -1351,7 +1626,8 @@ daemon_msg_findallif_req(uint8 ver, struct daemon_slpars *pars, uint32 plen) if (alldevs == NULL) { - if (rpcap_senderror(pars->sockctrl, ver, PCAP_ERR_NOREMOTEIF, + if (rpcap_senderror(pars->sockctrl, pars->ssl, ver, + PCAP_ERR_NOREMOTEIF, "No interfaces found! Make sure libpcap/WinPcap is properly installed" " and you have the right to access to the remote device.", errbuf) == -1) @@ -1369,13 +1645,30 @@ daemon_msg_findallif_req(uint8 ver, struct daemon_slpars *pars, uint32 plen) { nif++; - if (d->description) - replylen += strlen(d->description); - if (d->name) - replylen += strlen(d->name); + if (d->description) { + size_t stringlen = strlen(d->description); + if (stringlen > UINT16_MAX) { + pcap_strlcpy(errmsgbuf, + "Description length doesn't fit in 16 bits", + sizeof (errmsgbuf)); + goto error; + } + CHECK_AND_INCREASE_REPLY_LEN(stringlen); + } + if (d->name) { + size_t stringlen = strlen(d->name); + if (stringlen > UINT16_MAX) { + pcap_strlcpy(errmsgbuf, + "Name length doesn't fit in 16 bits", + sizeof (errmsgbuf)); + goto error; + } + CHECK_AND_INCREASE_REPLY_LEN(stringlen); + } - replylen += sizeof(struct rpcap_findalldevs_if); + CHECK_AND_INCREASE_REPLY_LEN(sizeof(struct rpcap_findalldevs_if)); + uint16_t naddrs = 0; for (address = d->addresses; address != NULL; address = address->next) { /* @@ -1387,7 +1680,14 @@ daemon_msg_findallif_req(uint8 ver, struct daemon_slpars *pars, uint32 plen) #ifdef AF_INET6 case AF_INET6: #endif - replylen += (sizeof(struct rpcap_sockaddr) * 4); + CHECK_AND_INCREASE_REPLY_LEN(sizeof(struct rpcap_sockaddr) * 4); + if (naddrs == UINT16_MAX) { + pcap_strlcpy(errmsgbuf, + "Number of interfaces doesn't fit in 16 bits", + sizeof (errmsgbuf)); + goto error; + } + naddrs++; break; default: @@ -1396,7 +1696,7 @@ daemon_msg_findallif_req(uint8 ver, struct daemon_slpars *pars, uint32 plen) } } - // RPCAP findalldevs command + // RPCAP findalldevs reply if (sock_bufferize(NULL, sizeof(struct rpcap_header), NULL, &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errmsgbuf, PCAP_ERRBUF_SIZE) == -1) @@ -1410,23 +1710,36 @@ daemon_msg_findallif_req(uint8 ver, struct daemon_slpars *pars, uint32 plen) { uint16 lname, ldescr; - findalldevs_if = (struct rpcap_findalldevs_if *) &sendbuf[sendbufidx]; + // Note: the findalldevs_if entries are *not* neatly + // aligned on 4-byte boundaries, because they're + // preceded by strings that aren't padded to 4-byte + // boundaries, so we cannot just cast output buffer + // boundaries to struct rpcap_findalldevs_if pointers + // and store into them - we must fill in a structure and + // then copy the structure to the buffer, as not all + // systems support unaligned access (some, such as + // SPARC, crash; others, such as Arm, may just ignore + // the lower-order bits). + struct rpcap_findalldevs_if findalldevs_if; - if (sock_bufferize(NULL, sizeof(struct rpcap_findalldevs_if), NULL, - &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errmsgbuf, PCAP_ERRBUF_SIZE) == -1) - goto error; + /* + * We've already established that the string lengths + * fit in 16 bits. + */ + if (d->description) + ldescr = (uint16) strlen(d->description); + else + ldescr = 0; + if (d->name) + lname = (uint16) strlen(d->name); + else + lname = 0; - memset(findalldevs_if, 0, sizeof(struct rpcap_findalldevs_if)); - - if (d->description) ldescr = (short) strlen(d->description); - else ldescr = 0; - if (d->name) lname = (short) strlen(d->name); - else lname = 0; - - findalldevs_if->desclen = htons(ldescr); - findalldevs_if->namelen = htons(lname); - findalldevs_if->flags = htonl(d->flags); + findalldevs_if.desclen = htons(ldescr); + findalldevs_if.namelen = htons(lname); + findalldevs_if.flags = htonl(d->flags); + uint16_t naddrs = 0; for (address = d->addresses; address != NULL; address = address->next) { /* @@ -1438,14 +1751,20 @@ daemon_msg_findallif_req(uint8 ver, struct daemon_slpars *pars, uint32 plen) #ifdef AF_INET6 case AF_INET6: #endif - findalldevs_if->naddr++; + naddrs++; break; default: break; } } - findalldevs_if->naddr = htons(findalldevs_if->naddr); + findalldevs_if.naddr = htons(naddrs); + findalldevs_if.dummy = 0; + + if (sock_bufferize(&findalldevs_if, sizeof(struct rpcap_findalldevs_if), sendbuf, + &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_BUFFERIZE, errmsgbuf, + PCAP_ERRBUF_SIZE) == -1) + goto error; if (sock_bufferize(d->name, lname, sendbuf, &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_BUFFERIZE, errmsgbuf, @@ -1506,7 +1825,7 @@ daemon_msg_findallif_req(uint8 ver, struct daemon_slpars *pars, uint32 plen) pcap_freealldevs(alldevs); // Send a final command that says "now send it!" - if (sock_send(pars->sockctrl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) + if (sock_send(pars->sockctrl, pars->ssl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) { rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); return -1; @@ -1518,8 +1837,8 @@ error: if (alldevs) pcap_freealldevs(alldevs); - if (rpcap_senderror(pars->sockctrl, ver, PCAP_ERR_FINDALLIF, - errmsgbuf, errbuf) == -1) + if (rpcap_senderror(pars->sockctrl, pars->ssl, ver, + PCAP_ERR_FINDALLIF, errmsgbuf, errbuf) == -1) { rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); return -1; @@ -1545,11 +1864,11 @@ daemon_msg_open_req(uint8 ver, struct daemon_slpars *pars, uint32 plen, if (plen > sourcelen - 1) { - pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Source string too long"); + snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Source string too long"); goto error; } - nread = sock_recv(pars->sockctrl, source, plen, + nread = sock_recv(pars->sockctrl, pars->ssl, source, plen, SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf, PCAP_ERRBUF_SIZE); if (nread == -1) { @@ -1563,7 +1882,7 @@ daemon_msg_open_req(uint8 ver, struct daemon_slpars *pars, uint32 plen, // If so, reject it. if (is_url(source)) { - pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Source string refers to a remote device"); + snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Source string refers to a remote device"); goto error; } @@ -1571,7 +1890,7 @@ daemon_msg_open_req(uint8 ver, struct daemon_slpars *pars, uint32 plen, // This is a fake open, since we do that only to get the needed parameters, then we close the device again if ((fp = pcap_open_live(source, 1500 /* fake snaplen */, - 0 /* no promis */, + 0 /* no promisc */, 1000 /* fake timeout */, errmsgbuf)) == NULL) goto error; @@ -1592,13 +1911,20 @@ daemon_msg_open_req(uint8 ver, struct daemon_slpars *pars, uint32 plen, memset(openreply, 0, sizeof(struct rpcap_openreply)); openreply->linktype = htonl(pcap_datalink(fp)); - openreply->tzoff = 0; /* This is always 0 for live captures */ + /* + * This is always 0 for live captures; we no longer support it + * as something we read from capture files and supply to + * clients, but we have to send it over the wire, as open + * replies are expected to have 8 bytes of payload by + * existing clients. + */ + openreply->tzoff = 0; // We're done with the pcap_t. pcap_close(fp); // Send the reply. - if (sock_send(pars->sockctrl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) + if (sock_send(pars->sockctrl, pars->ssl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) { rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); return -1; @@ -1606,7 +1932,7 @@ daemon_msg_open_req(uint8 ver, struct daemon_slpars *pars, uint32 plen, return 0; error: - if (rpcap_senderror(pars->sockctrl, ver, PCAP_ERR_OPEN, + if (rpcap_senderror(pars->sockctrl, pars->ssl, ver, PCAP_ERR_OPEN, errmsgbuf, errbuf) == -1) { // That failed; log a message and give up. @@ -1615,7 +1941,7 @@ error: } // Check if all the data has been read; if not, discard the data in excess - if (rpcapd_discard(pars->sockctrl, plen) == -1) + if (rpcapd_discard(pars->sockctrl, pars->ssl, plen) == -1) { return -1; } @@ -1629,7 +1955,7 @@ error: static int daemon_msg_startcap_req(uint8 ver, struct daemon_slpars *pars, uint32 plen, char *source, struct session **sessionp, - struct rpcap_sampling *samp_param _U_) + struct rpcap_sampling *samp_param _U_, int uses_ssl) { char errbuf[PCAP_ERRBUF_SIZE]; // buffer for network errors char errmsgbuf[PCAP_ERRBUF_SIZE]; // buffer for errors to send to the client @@ -1654,7 +1980,7 @@ daemon_msg_startcap_req(uint8 ver, struct daemon_slpars *pars, uint32 plen, addrinfo = NULL; - status = rpcapd_recv(pars->sockctrl, (char *) &startcapreq, + status = rpcapd_recv(pars->sockctrl, pars->ssl, (char *) &startcapreq, sizeof(struct rpcap_startcapreq), &plen, errmsgbuf); if (status == -1) { @@ -1667,15 +1993,25 @@ daemon_msg_startcap_req(uint8 ver, struct daemon_slpars *pars, uint32 plen, startcapreq.flags = ntohs(startcapreq.flags); + // Check that the client does not ask for UDP is the server has been asked + // to enforce encryption, as SSL is not supported yet with UDP: + if (uses_ssl && (startcapreq.flags & RPCAP_STARTCAPREQ_FLAG_DGRAM)) + { + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "SSL not supported with UDP forward of remote packets"); + goto error; + } + // Create a session structure session = malloc(sizeof(struct session)); if (session == NULL) { - pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Can't allocate session structure"); + snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Can't allocate session structure"); goto error; } session->sockdata = INVALID_SOCKET; + session->ctrl_ssl = session->data_ssl = NULL; // We don't have a thread yet. session->have_thread = 0; // @@ -1726,7 +2062,8 @@ daemon_msg_startcap_req(uint8 ver, struct daemon_slpars *pars, uint32 plen, saddrlen = sizeof(struct sockaddr_storage); if (getpeername(pars->sockctrl, (struct sockaddr *) &saddr, &saddrlen) == -1) { - sock_geterror("getpeername()", errmsgbuf, PCAP_ERRBUF_SIZE); + sock_geterrmsg(errmsgbuf, PCAP_ERRBUF_SIZE, + "getpeername() failed"); goto error; } @@ -1737,38 +2074,40 @@ daemon_msg_startcap_req(uint8 ver, struct daemon_slpars *pars, uint32 plen, // Now we have to create a new socket to send packets if (serveropen_dp) // Data connection is opened by the server toward the client { - pcap_snprintf(portdata, sizeof portdata, "%d", ntohs(startcapreq.portdata)); + snprintf(portdata, sizeof portdata, "%d", ntohs(startcapreq.portdata)); // Get the name of the other peer (needed to connect to that specific network address) if (getnameinfo((struct sockaddr *) &saddr, saddrlen, peerhost, sizeof(peerhost), NULL, 0, NI_NUMERICHOST)) { - sock_geterror("getnameinfo()", errmsgbuf, PCAP_ERRBUF_SIZE); + sock_geterrmsg(errmsgbuf, PCAP_ERRBUF_SIZE, + "getnameinfo() failed"); goto error; } if (sock_initaddress(peerhost, portdata, &hints, &addrinfo, errmsgbuf, PCAP_ERRBUF_SIZE) == -1) goto error; - if ((session->sockdata = sock_open(addrinfo, SOCKOPEN_CLIENT, 0, errmsgbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET) + if ((session->sockdata = sock_open(peerhost, addrinfo, SOCKOPEN_CLIENT, 0, errmsgbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET) goto error; } else // Data connection is opened by the client toward the server { hints.ai_flags = AI_PASSIVE; - // Let's the server socket pick up a free network port for us - if (sock_initaddress(NULL, "0", &hints, &addrinfo, errmsgbuf, PCAP_ERRBUF_SIZE) == -1) + // Make the server socket pick up a free network port for us + if (sock_initaddress(NULL, NULL, &hints, &addrinfo, errmsgbuf, PCAP_ERRBUF_SIZE) == -1) goto error; - if ((session->sockdata = sock_open(addrinfo, SOCKOPEN_SERVER, 1 /* max 1 connection in queue */, errmsgbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET) + if ((session->sockdata = sock_open(NULL, addrinfo, SOCKOPEN_SERVER, 1 /* max 1 connection in queue */, errmsgbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET) goto error; // get the complete sockaddr structure used in the data connection saddrlen = sizeof(struct sockaddr_storage); if (getsockname(session->sockdata, (struct sockaddr *) &saddr, &saddrlen) == -1) { - sock_geterror("getsockname()", errmsgbuf, PCAP_ERRBUF_SIZE); + sock_geterrmsg(errmsgbuf, PCAP_ERRBUF_SIZE, + "getsockname() failed"); goto error; } @@ -1776,7 +2115,8 @@ daemon_msg_startcap_req(uint8 ver, struct daemon_slpars *pars, uint32 plen, if (getnameinfo((struct sockaddr *) &saddr, saddrlen, NULL, 0, portdata, sizeof(portdata), NI_NUMERICSERV)) { - sock_geterror("getnameinfo()", errmsgbuf, PCAP_ERRBUF_SIZE); + sock_geterrmsg(errmsgbuf, PCAP_ERRBUF_SIZE, + "getnameinfo() failed"); goto error; } } @@ -1787,10 +2127,11 @@ daemon_msg_startcap_req(uint8 ver, struct daemon_slpars *pars, uint32 plen, // Needed to send an error on the ctrl connection session->sockctrl = pars->sockctrl; + session->ctrl_ssl = pars->ssl; session->protocol_version = ver; // Now I can set the filter - ret = daemon_unpackapplyfilter(pars->sockctrl, session, &plen, errmsgbuf); + ret = daemon_unpackapplyfilter(pars->sockctrl, pars->ssl, session, &plen, errmsgbuf); if (ret == -1) { // Fatal error. A message has been logged; just give up. @@ -1825,7 +2166,7 @@ daemon_msg_startcap_req(uint8 ver, struct daemon_slpars *pars, uint32 plen, startcapreply->portdata = htons(port); } - if (sock_send(pars->sockctrl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) + if (sock_send(pars->sockctrl, pars->ssl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) { // That failed; log a message and give up. rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); @@ -1843,7 +2184,8 @@ daemon_msg_startcap_req(uint8 ver, struct daemon_slpars *pars, uint32 plen, if (socktemp == INVALID_SOCKET) { - sock_geterror("accept()", errbuf, PCAP_ERRBUF_SIZE); + sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE, + "accept() failed"); rpcapd_log(LOGPRIO_ERROR, "Accept of data connection failed: %s", errbuf); goto error; @@ -1854,13 +2196,30 @@ daemon_msg_startcap_req(uint8 ver, struct daemon_slpars *pars, uint32 plen, session->sockdata = socktemp; } + SSL *ssl = NULL; + if (uses_ssl) + { +#ifdef HAVE_OPENSSL + /* In both active or passive cases, wait for the client to initiate the + * TLS handshake. Yes during that time the control socket will not be + * served, but the same was true from the above call to accept(). */ + ssl = ssl_promotion(1, session->sockdata, errbuf, PCAP_ERRBUF_SIZE); + if (! ssl) + { + rpcapd_log(LOGPRIO_ERROR, "TLS handshake failed: %s", errbuf); + goto error; + } +#endif + } + session->data_ssl = ssl; + // Now we have to create a new thread to receive packets #ifdef _WIN32 session->thread = (HANDLE)_beginthreadex(NULL, 0, daemon_thrdatamain, (void *) session, 0, NULL); if (session->thread == 0) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error creating the data thread"); + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error creating the data thread"); goto error; } #else @@ -1876,7 +2235,7 @@ daemon_msg_startcap_req(uint8 ver, struct daemon_slpars *pars, uint32 plen, session->have_thread = 1; // Check if all the data has been read; if not, discard the data in excess - if (rpcapd_discard(pars->sockctrl, plen) == -1) + if (rpcapd_discard(pars->sockctrl, pars->ssl, plen) == -1) goto fatal_error; *sessionp = session; @@ -1898,8 +2257,8 @@ error: free(session); } - if (rpcap_senderror(pars->sockctrl, ver, PCAP_ERR_STARTCAPTURE, - errmsgbuf, errbuf) == -1) + if (rpcap_senderror(pars->sockctrl, pars->ssl, ver, + PCAP_ERR_STARTCAPTURE, errmsgbuf, errbuf) == -1) { // That failed; log a message and give up. rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); @@ -1907,7 +2266,7 @@ error: } // Check if all the data has been read; if not, discard the data in excess - if (rpcapd_discard(pars->sockctrl, plen) == -1) + if (rpcapd_discard(pars->sockctrl, pars->ssl, plen) == -1) { // Network error. return -1; @@ -1942,7 +2301,7 @@ daemon_msg_endcap_req(uint8 ver, struct daemon_slpars *pars, rpcap_createhdr(&header, ver, RPCAP_MSG_ENDCAP_REPLY, 0, 0); - if (sock_send(pars->sockctrl, (char *) &header, sizeof(struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) == -1) + if (sock_send(pars->sockctrl, pars->ssl, (char *) &header, sizeof(struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) == -1) { // That failed; log a message and give up. rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); @@ -1970,7 +2329,7 @@ daemon_msg_endcap_req(uint8 ver, struct daemon_slpars *pars, #define RPCAP_BPF_MAXINSNS 8192 static int -daemon_unpackapplyfilter(SOCKET sockctrl, struct session *session, uint32 *plenp, char *errmsgbuf) +daemon_unpackapplyfilter(SOCKET sockctrl, SSL *ctrl_ssl, struct session *session, uint32 *plenp, char *errmsgbuf) { int status; struct rpcap_filter filter; @@ -1979,7 +2338,7 @@ daemon_unpackapplyfilter(SOCKET sockctrl, struct session *session, uint32 *plenp struct bpf_program bf_prog; unsigned int i; - status = rpcapd_recv(sockctrl, (char *) &filter, + status = rpcapd_recv(sockctrl, ctrl_ssl, (char *) &filter, sizeof(struct rpcap_filter), plenp, errmsgbuf); if (status == -1) { @@ -1994,14 +2353,14 @@ daemon_unpackapplyfilter(SOCKET sockctrl, struct session *session, uint32 *plenp if (ntohs(filter.filtertype) != RPCAP_UPDATEFILTER_BPF) { - pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Only BPF/NPF filters are currently supported"); + snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Only BPF/NPF filters are currently supported"); return -2; } if (bf_prog.bf_len > RPCAP_BPF_MAXINSNS) { - pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, - "Filter program is larger than the maximum size of %u instructions", + snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, + "Filter program is larger than the maximum size of %d instructions", RPCAP_BPF_MAXINSNS); return -2; } @@ -2017,7 +2376,7 @@ daemon_unpackapplyfilter(SOCKET sockctrl, struct session *session, uint32 *plenp for (i = 0; i < bf_prog.bf_len; i++) { - status = rpcapd_recv(sockctrl, (char *) &insn, + status = rpcapd_recv(sockctrl, ctrl_ssl, (char *) &insn, sizeof(struct rpcap_filterbpf_insn), plenp, errmsgbuf); if (status == -1) { @@ -2036,15 +2395,18 @@ daemon_unpackapplyfilter(SOCKET sockctrl, struct session *session, uint32 *plenp bf_insn++; } + // + // XXX - pcap_setfilter() should do the validation for us. + // if (bpf_validate(bf_prog.bf_insns, bf_prog.bf_len) == 0) { - pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "The filter contains bogus instructions"); + snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "The filter contains bogus instructions"); return -2; } if (pcap_setfilter(session->fp, &bf_prog)) { - pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "RPCAP error: %s", pcap_geterr(session->fp)); + snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "RPCAP error: %s", pcap_geterr(session->fp)); return -2; } @@ -2060,7 +2422,7 @@ daemon_msg_updatefilter_req(uint8 ver, struct daemon_slpars *pars, int ret; // status of daemon_unpackapplyfilter() struct rpcap_header header; // keeps the answer to the updatefilter command - ret = daemon_unpackapplyfilter(pars->sockctrl, session, &plen, errmsgbuf); + ret = daemon_unpackapplyfilter(pars->sockctrl, pars->ssl, session, &plen, errmsgbuf); if (ret == -1) { // Fatal error. A message has been logged; just give up. @@ -2073,7 +2435,7 @@ daemon_msg_updatefilter_req(uint8 ver, struct daemon_slpars *pars, } // Check if all the data has been read; if not, discard the data in excess - if (rpcapd_discard(pars->sockctrl, plen) == -1) + if (rpcapd_discard(pars->sockctrl, pars->ssl, plen) == -1) { // Network error. return -1; @@ -2082,9 +2444,9 @@ daemon_msg_updatefilter_req(uint8 ver, struct daemon_slpars *pars, // A response is needed, otherwise the other host does not know that everything went well rpcap_createhdr(&header, ver, RPCAP_MSG_UPDATEFILTER_REPLY, 0, 0); - if (sock_send(pars->sockctrl, (char *) &header, sizeof (struct rpcap_header), pcap_geterr(session->fp), PCAP_ERRBUF_SIZE)) + if (sock_send(pars->sockctrl, pars->ssl, (char *) &header, sizeof (struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE)) { - // That failed; log a messsage and give up. + // That failed; log a message and give up. rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); return -1; } @@ -2092,11 +2454,11 @@ daemon_msg_updatefilter_req(uint8 ver, struct daemon_slpars *pars, return 0; error: - if (rpcapd_discard(pars->sockctrl, plen) == -1) + if (rpcapd_discard(pars->sockctrl, pars->ssl, plen) == -1) { return -1; } - rpcap_senderror(pars->sockctrl, ver, PCAP_ERR_UPDATEFILTER, + rpcap_senderror(pars->sockctrl, pars->ssl, ver, PCAP_ERR_UPDATEFILTER, errmsgbuf, NULL); return 0; @@ -2115,7 +2477,7 @@ daemon_msg_setsampling_req(uint8 ver, struct daemon_slpars *pars, uint32 plen, struct rpcap_sampling rpcap_samp; int status; - status = rpcapd_recv(pars->sockctrl, (char *) &rpcap_samp, sizeof(struct rpcap_sampling), &plen, errmsgbuf); + status = rpcapd_recv(pars->sockctrl, pars->ssl, (char *) &rpcap_samp, sizeof(struct rpcap_sampling), &plen, errmsgbuf); if (status == -1) { return -1; @@ -2132,14 +2494,14 @@ daemon_msg_setsampling_req(uint8 ver, struct daemon_slpars *pars, uint32 plen, // A response is needed, otherwise the other host does not know that everything went well rpcap_createhdr(&header, ver, RPCAP_MSG_SETSAMPLING_REPLY, 0, 0); - if (sock_send(pars->sockctrl, (char *) &header, sizeof (struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) == -1) + if (sock_send(pars->sockctrl, pars->ssl, (char *) &header, sizeof (struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) == -1) { - // That failed; log a messsage and give up. + // That failed; log a message and give up. rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); return -1; } - if (rpcapd_discard(pars->sockctrl, plen) == -1) + if (rpcapd_discard(pars->sockctrl, pars->ssl, plen) == -1) { return -1; } @@ -2147,7 +2509,7 @@ daemon_msg_setsampling_req(uint8 ver, struct daemon_slpars *pars, uint32 plen, return 0; error: - if (rpcap_senderror(pars->sockctrl, ver, PCAP_ERR_SETSAMPLING, + if (rpcap_senderror(pars->sockctrl, pars->ssl, ver, PCAP_ERR_SETSAMPLING, errmsgbuf, errbuf) == -1) { // That failed; log a message and give up. @@ -2156,7 +2518,7 @@ error: } // Check if all the data has been read; if not, discard the data in excess - if (rpcapd_discard(pars->sockctrl, plen) == -1) + if (rpcapd_discard(pars->sockctrl, pars->ssl, plen) == -1) { return -1; } @@ -2176,7 +2538,7 @@ daemon_msg_stats_req(uint8 ver, struct daemon_slpars *pars, struct rpcap_stats *netstats; // statistics sent on the network // Checks that the header does not contain other data; if so, discard it - if (rpcapd_discard(pars->sockctrl, plen) == -1) + if (rpcapd_discard(pars->sockctrl, pars->ssl, plen) == -1) { // Network error. return -1; @@ -2199,7 +2561,7 @@ daemon_msg_stats_req(uint8 ver, struct daemon_slpars *pars, { if (pcap_stats(session->fp, stats) == -1) { - pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "%s", pcap_geterr(session->fp)); + snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "%s", pcap_geterr(session->fp)); goto error; } @@ -2220,7 +2582,7 @@ daemon_msg_stats_req(uint8 ver, struct daemon_slpars *pars, } // Send the packet - if (sock_send(pars->sockctrl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) + if (sock_send(pars->sockctrl, pars->ssl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) { rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); return -1; @@ -2229,7 +2591,7 @@ daemon_msg_stats_req(uint8 ver, struct daemon_slpars *pars, return 0; error: - rpcap_senderror(pars->sockctrl, ver, PCAP_ERR_GETSTATS, + rpcap_senderror(pars->sockctrl, pars->ssl, ver, PCAP_ERR_GETSTATS, errmsgbuf, NULL); return 0; } @@ -2288,7 +2650,21 @@ daemon_thrdatamain(void *ptr) // // So we don't need to make sure that sendbufsize will overflow. // + // However, we *do* need to make sure its value fits in an int, + // because sock_send() can't send more than INT_MAX bytes (it could + // do so on 64-bit UN*Xes, but can't do so on Windows, not even + // 64-bit Windows, as the send() buffer size argument is an int + // in Winsock). + // sendbufsize = sizeof(struct rpcap_header) + sizeof(struct rpcap_pkthdr) + pcap_snapshot(session->fp); + if (sendbufsize > INT_MAX) + { + rpcapd_log(LOGPRIO_ERROR, + "Buffer size for this child thread would be larger than %d", + INT_MAX); + sendbuf = NULL; // we haven't allocated a buffer, so nothing to free + goto error; + } sendbuf = (char *) malloc (sendbufsize); if (sendbuf == NULL) { @@ -2335,7 +2711,7 @@ daemon_thrdatamain(void *ptr) // Bufferize the general header if (sock_bufferize(NULL, sizeof(struct rpcap_header), NULL, - &sendbufidx, sendbufsize, SOCKBUF_CHECKONLY, errbuf, + &sendbufidx, (int)sendbufsize, SOCKBUF_CHECKONLY, errbuf, PCAP_ERRBUF_SIZE) == -1) { rpcapd_log(LOGPRIO_ERROR, @@ -2352,7 +2728,7 @@ daemon_thrdatamain(void *ptr) // Bufferize the pkt header if (sock_bufferize(NULL, sizeof(struct rpcap_pkthdr), NULL, - &sendbufidx, sendbufsize, SOCKBUF_CHECKONLY, errbuf, + &sendbufidx, (int)sendbufsize, SOCKBUF_CHECKONLY, errbuf, PCAP_ERRBUF_SIZE) == -1) { rpcapd_log(LOGPRIO_ERROR, @@ -2364,12 +2740,16 @@ daemon_thrdatamain(void *ptr) net_pkt_header->caplen = htonl(pkt_header->caplen); net_pkt_header->len = htonl(pkt_header->len); net_pkt_header->npkt = htonl(++(session->TotCapt)); - net_pkt_header->timestamp_sec = htonl(pkt_header->ts.tv_sec); - net_pkt_header->timestamp_usec = htonl(pkt_header->ts.tv_usec); + // + // This protocol needs to be updated with a new version + // before 2038-01-19 03:14:07 UTC. + // + net_pkt_header->timestamp_sec = htonl((uint32)pkt_header->ts.tv_sec); + net_pkt_header->timestamp_usec = htonl((uint32)pkt_header->ts.tv_usec); // Bufferize the pkt data if (sock_bufferize((char *) pkt_data, pkt_header->caplen, - sendbuf, &sendbufidx, sendbufsize, SOCKBUF_BUFFERIZE, + sendbuf, &sendbufidx, (int)sendbufsize, SOCKBUF_BUFFERIZE, errbuf, PCAP_ERRBUF_SIZE) == -1) { rpcapd_log(LOGPRIO_ERROR, @@ -2381,7 +2761,7 @@ daemon_thrdatamain(void *ptr) // Send the packet // If the client dropped the connection, don't report an // error, just quit. - status = sock_send(session->sockdata, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE); + status = sock_send(session->sockdata, session->data_ssl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE); if (status < 0) { if (status == -1) @@ -2412,8 +2792,8 @@ daemon_thrdatamain(void *ptr) // The latter just means that the client told us to stop // capturing, so there's no error to report. // - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error reading the packets: %s", pcap_geterr(session->fp)); - rpcap_senderror(session->sockctrl, session->protocol_version, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error reading the packets: %s", pcap_geterr(session->fp)); + rpcap_senderror(session->sockctrl, session->ctrl_ssl, session->protocol_version, PCAP_ERR_READEX, errbuf, NULL); } @@ -2461,19 +2841,25 @@ daemon_seraddr(struct sockaddr_storage *sockaddrin, struct rpcap_sockaddr *socka if (sockaddrin == NULL) return; // Warning: we support only AF_INET and AF_INET6 + // + // Note: as noted above, the output structures are not + // neatly aligned on 4-byte boundaries, so we must fill + // in an aligned structure and then copy it to the output + // buffer with memcpy(). switch (sockaddrin->ss_family) { case AF_INET: { struct sockaddr_in *sockaddrin_ipv4; - struct rpcap_sockaddr_in *sockaddrout_ipv4; + struct rpcap_sockaddr_in sockaddrout_ipv4; sockaddrin_ipv4 = (struct sockaddr_in *) sockaddrin; - sockaddrout_ipv4 = (struct rpcap_sockaddr_in *) sockaddrout; - sockaddrout_ipv4->family = htons(RPCAP_AF_INET); - sockaddrout_ipv4->port = htons(sockaddrin_ipv4->sin_port); - memcpy(&sockaddrout_ipv4->addr, &sockaddrin_ipv4->sin_addr, sizeof(sockaddrout_ipv4->addr)); - memset(sockaddrout_ipv4->zero, 0, sizeof(sockaddrout_ipv4->zero)); + + sockaddrout_ipv4.family = htons(RPCAP_AF_INET); + sockaddrout_ipv4.port = htons(sockaddrin_ipv4->sin_port); + memcpy(&sockaddrout_ipv4.addr, &sockaddrin_ipv4->sin_addr, sizeof(sockaddrout_ipv4.addr)); + memset(sockaddrout_ipv4.zero, 0, sizeof(sockaddrout_ipv4.zero)); + memcpy(sockaddrout, &sockaddrout_ipv4, sizeof(struct rpcap_sockaddr_in)); break; } @@ -2481,15 +2867,16 @@ daemon_seraddr(struct sockaddr_storage *sockaddrin, struct rpcap_sockaddr *socka case AF_INET6: { struct sockaddr_in6 *sockaddrin_ipv6; - struct rpcap_sockaddr_in6 *sockaddrout_ipv6; + struct rpcap_sockaddr_in6 sockaddrout_ipv6; sockaddrin_ipv6 = (struct sockaddr_in6 *) sockaddrin; - sockaddrout_ipv6 = (struct rpcap_sockaddr_in6 *) sockaddrout; - sockaddrout_ipv6->family = htons(RPCAP_AF_INET6); - sockaddrout_ipv6->port = htons(sockaddrin_ipv6->sin6_port); - sockaddrout_ipv6->flowinfo = htonl(sockaddrin_ipv6->sin6_flowinfo); - memcpy(&sockaddrout_ipv6->addr, &sockaddrin_ipv6->sin6_addr, sizeof(sockaddrout_ipv6->addr)); - sockaddrout_ipv6->scope_id = htonl(sockaddrin_ipv6->sin6_scope_id); + + sockaddrout_ipv6.family = htons(RPCAP_AF_INET6); + sockaddrout_ipv6.port = htons(sockaddrin_ipv6->sin6_port); + sockaddrout_ipv6.flowinfo = htonl(sockaddrin_ipv6->sin6_flowinfo); + memcpy(&sockaddrout_ipv6.addr, &sockaddrin_ipv6->sin6_addr, sizeof(sockaddrout_ipv6.addr)); + sockaddrout_ipv6.scope_id = htonl(sockaddrin_ipv6->sin6_scope_id); + memcpy(sockaddrout, &sockaddrout_ipv6, sizeof(struct rpcap_sockaddr_in6)); break; } #endif @@ -2502,6 +2889,7 @@ daemon_seraddr(struct sockaddr_storage *sockaddrin, struct rpcap_sockaddr *socka */ void sleep_secs(int secs) { +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION #ifdef _WIN32 Sleep(secs*1000); #else @@ -2513,18 +2901,19 @@ void sleep_secs(int secs) while (secs_remaining != 0) secs_remaining = sleep(secs_remaining); #endif +#endif } /* * Read the header of a message. */ static int -rpcapd_recv_msg_header(SOCKET sock, struct rpcap_header *headerp) +rpcapd_recv_msg_header(SOCKET sock, SSL *ssl, struct rpcap_header *headerp) { int nread; char errbuf[PCAP_ERRBUF_SIZE]; // buffer for network errors - nread = sock_recv(sock, (char *) headerp, sizeof(struct rpcap_header), + nread = sock_recv(sock, ssl, (char *) headerp, sizeof(struct rpcap_header), SOCK_RECEIVEALL_YES|SOCK_EOF_ISNT_ERROR, errbuf, PCAP_ERRBUF_SIZE); if (nread == -1) { @@ -2551,7 +2940,7 @@ rpcapd_recv_msg_header(SOCKET sock, struct rpcap_header *headerp) * error. */ static int -rpcapd_recv(SOCKET sock, char *buffer, size_t toread, uint32 *plen, char *errmsgbuf) +rpcapd_recv(SOCKET sock, SSL *ssl, char *buffer, size_t toread, uint32 *plen, char *errmsgbuf) { int nread; char errbuf[PCAP_ERRBUF_SIZE]; // buffer for network errors @@ -2559,10 +2948,10 @@ rpcapd_recv(SOCKET sock, char *buffer, size_t toread, uint32 *plen, char *errmsg if (toread > *plen) { // Tell the client and continue. - pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Message payload is too short"); + snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Message payload is too short"); return -2; } - nread = sock_recv(sock, buffer, toread, + nread = sock_recv(sock, ssl, buffer, toread, SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf, PCAP_ERRBUF_SIZE); if (nread == -1) { @@ -2580,13 +2969,13 @@ rpcapd_recv(SOCKET sock, char *buffer, size_t toread, uint32 *plen, char *errmsg * error. */ static int -rpcapd_discard(SOCKET sock, uint32 len) +rpcapd_discard(SOCKET sock, SSL *ssl, uint32 len) { char errbuf[PCAP_ERRBUF_SIZE + 1]; // keeps the error string, prior to be printed if (len != 0) { - if (sock_discard(sock, len, errbuf, PCAP_ERRBUF_SIZE) == -1) + if (sock_discard(sock, ssl, len, errbuf, PCAP_ERRBUF_SIZE) == -1) { // Network error. rpcapd_log(LOGPRIO_ERROR, "Read from client failed: %s", errbuf); @@ -2669,6 +3058,16 @@ static void session_close(struct session *session) #endif } +#ifdef HAVE_OPENSSL + if (session->data_ssl) + { + // Finish using the SSL handle for the socket. + // This must be done *before* the socket is closed. + ssl_finish(session->data_ssl); + session->data_ssl = NULL; + } +#endif + if (session->sockdata != INVALID_SOCKET) { sock_close(session->sockdata, NULL, 0); diff --git a/rpcapd/daemon.h b/rpcapd/daemon.h index 74e17da5a505..dbbdb62ca1db 100644 --- a/rpcapd/daemon.h +++ b/rpcapd/daemon.h @@ -33,13 +33,19 @@ #ifndef __DAEMON_H__ #define __DAEMON_H__ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "sslutils.h" + // // Returns 1 if the client closed the control connection explicitly, 0 // otherwise; the return value is used only by callers that call us // for active mode. // int daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, - int nullAuthAllowed); + int nullAuthAllowed, int uses_ssl); void sleep_secs(int secs); diff --git a/rpcapd/fileconf.c b/rpcapd/fileconf.c index 2f15c014204f..c051f887ead6 100644 --- a/rpcapd/fileconf.c +++ b/rpcapd/fileconf.c @@ -39,7 +39,6 @@ #include #include -#include #include #include // for PCAP_ERRBUF_SIZE @@ -59,6 +58,16 @@ static char *skipws(char *ptr); +/* + * Locale-independent version checks for alphabetical and alphanumerical + * characters that also can handle being handed a char value that might + * be negative. + */ +#define FILECONF_ISALPHA(c) \ + (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z')) +#define FILECONF_ISALNUM(c) \ + (FILECONF_ISALPHA(c) || ((c) >= '0' && (c) <= '9')) + void fileconf_read(void) { FILE *fp; @@ -135,8 +144,7 @@ void fileconf_read(void) // Is the next character alphabetic? If not, // this isn't a valid parameter name. // - if (!isascii((unsigned char)*ptr) || - !isalpha((unsigned char)*ptr)) + if (FILECONF_ISALPHA(*ptr)) { rpcapd_log(LOGPRIO_ERROR, "%s, line %u doesn't have a valid parameter name", @@ -150,8 +158,7 @@ void fileconf_read(void) // That's the name of the parameter being set. // param = ptr; - while (isascii((unsigned char)*ptr) && - (isalnum((unsigned char)*ptr) || *ptr == '-' || *ptr == '_')) + while (FILECONF_ISALNUM(*ptr) || *ptr == '-' || *ptr == '_') ptr++; // @@ -234,13 +241,15 @@ void fileconf_read(void) ptr += toklen; // skip to the terminator if (toklen == 0) { - if (isascii((unsigned char)*ptr) && - (isspace((unsigned char)*ptr) || *ptr == '#' || *ptr == '\0')) + if (*ptr == ' ' || *ptr == '\t' || + *ptr == '\r' || *ptr == '\n' || + *ptr == '#' || *ptr == '\0') { // // The first character it saw // was a whitespace character - // or a comment character. + // or a comment character, + // or we ran out of characters. // This means that there's // no value. // @@ -377,7 +386,7 @@ void fileconf_read(void) // // Append this to the host list. - // Save the curren end-of-string for the + // Save the current end-of-string for the // host list, in case the new host doesn't // fit, so that we can discard the partially- // copied host name. @@ -498,8 +507,7 @@ int fileconf_save(const char *savefile) fprintf(fp, "# Hosts which are allowed to connect to this server (passive mode)\n"); fprintf(fp, "# Format: PassiveClient = \n\n"); - strncpy(temphostlist, hostlist, MAX_HOST_LIST); - temphostlist[MAX_HOST_LIST] = 0; + pcap_strlcpy(temphostlist, hostlist, sizeof (temphostlist)); token = pcap_strtok_r(temphostlist, RPCAP_HOSTLIST_SEP, &lasts); while(token != NULL) @@ -548,7 +556,7 @@ int fileconf_save(const char *savefile) // static char *skipws(char *ptr) { - while (isascii((unsigned char)*ptr) && isspace((unsigned char)*ptr)) { + while (*ptr == ' ' || *ptr == '\t' || *ptr == '\r' || *ptr == '\n') { if (*ptr == '\r' || *ptr == '\n') return NULL; *ptr++ = '\0'; diff --git a/rpcapd/log.c b/rpcapd/log.c index 7b5fee57caaf..f26c145e6ca1 100644 --- a/rpcapd/log.c +++ b/rpcapd/log.c @@ -229,7 +229,7 @@ static void rpcapd_vlog_systemlog(log_priority priority, const char *message, */ char logbuf[1024+1]; - pcap_vsnprintf(logbuf, sizeof logbuf, message, ap); + vsnprintf(logbuf, sizeof logbuf, message, ap); syslog(syslog_priority, "%s", logbuf); #endif } diff --git a/rpcapd/org.tcpdump.rpcapd.plist b/rpcapd/org.tcpdump.rpcapd.plist index db3223a70317..b0e24398b677 100644 --- a/rpcapd/org.tcpdump.rpcapd.plist +++ b/rpcapd/org.tcpdump.rpcapd.plist @@ -1,5 +1,5 @@ - + Disabled diff --git a/rpcapd/rpcapd-config.manfile.in b/rpcapd/rpcapd-config.manfile.in index 1a87529d8abf..267b48e5e642 100644 --- a/rpcapd/rpcapd-config.manfile.in +++ b/rpcapd/rpcapd-config.manfile.in @@ -21,7 +21,7 @@ .SH NAME rpcapd-config \- rpcapd configuration file format .SH DESCRIPTION -An +An .B rpcapd configuration file allows parameters to be set for .BR rpcapd (@MAN_ADMIN_COMMANDS@). @@ -42,7 +42,7 @@ are: .TP .B ActiveClient .I value -is a host name or IP addresse, followed by a comma, +is a host name or IP address, followed by a comma, semicolon, or space, followed by a port name and address or .BR DEFAULT . .B DEFAULT @@ -54,7 +54,7 @@ should connect in active mode. .TP .B PassiveClient .I value -is a host name or IP addresse, followed by a comma, +is a host name or IP address, followed by a comma, semicolon, or space, followed by a port name and address or .BR DEFAULT . .B DEFAULT @@ -75,4 +75,4 @@ means that null authentication is permitted; .B No means that it is not permitted. .SH SEE ALSO -rpcapd(@MAN_ADMIN_COMMANDS@) +.BR rpcapd (@MAN_ADMIN_COMMANDS@) diff --git a/rpcapd/rpcapd.c b/rpcapd/rpcapd.c index 430acdc88e86..4c1b74549a45 100644 --- a/rpcapd/rpcapd.c +++ b/rpcapd/rpcapd.c @@ -35,10 +35,12 @@ #endif #include "ftmacros.h" +#include "diag-control.h" #include // for the errno variable #include // for strtok, etc #include // for malloc(), free(), ... +#include // for fprintf(), stderr, FILE etc #include // for PCAP_ERRBUF_SIZE #include // for signal() @@ -53,6 +55,10 @@ #include "daemon.h" // the true main() method of this daemon #include "log.h" +#ifdef HAVE_OPENSSL +#include "sslutils.h" +#endif + #ifdef _WIN32 #include // for thread stuff #include "win32-svc.h" // for Win32 service stuff @@ -86,6 +92,7 @@ static HANDLE state_change_event; //!< event to signal that a state change shou #endif static volatile sig_atomic_t shutdown_server; //!< '1' if the server is to shut down static volatile sig_atomic_t reread_config; //!< '1' if the server is to re-read its configuration +static int uses_ssl; //!< '1' to use TLS over the data socket extern char *optarg; // for getopt() @@ -112,7 +119,7 @@ static unsigned __stdcall main_passive_serviceloop_thread(void *ptr); /*! \brief Prints the usage screen if it is launched in console mode. */ -static void printusage(void) +static void printusage(FILE * f) { const char *usagetext = "USAGE:" @@ -145,14 +152,23 @@ static void printusage(void) " -i run in inetd mode (UNIX only)\n\n" #endif " -D log debugging messages\n\n" +#ifdef HAVE_OPENSSL + " -S encrypt all communication with SSL (implements rpcaps://)\n" + " -C enable compression\n" + " -K uses the SSL private key in this file (default: key.pem)\n" + " -X uses the certificate from this file (default: cert.pem)\n" +#endif " -s save the current configuration to file\n\n" " -f load the current configuration from file; all switches\n" " specified from the command line are ignored\n\n" " -h print this help screen\n\n"; - (void)fprintf(stderr, "RPCAPD, a remote packet capture daemon.\n" - "Compiled with %s\n\n", pcap_lib_version()); - printf("%s", usagetext); + (void)fprintf(f, "RPCAPD, a remote packet capture daemon.\n" + "Compiled with %s\n", pcap_lib_version()); +#if defined(HAVE_OPENSSL) && defined(SSLEAY_VERSION) + (void)fprintf(f, "Compiled with %s\n", SSLeay_version(SSLEAY_VERSION)); +#endif + (void)fprintf(f, "\n%s", usagetext); } @@ -172,6 +188,9 @@ int main(int argc, char *argv[]) #ifndef _WIN32 struct sigaction action; #endif +#ifdef HAVE_OPENSSL + int enable_compression = 0; +#endif savefile[0] = 0; loadfile[0] = 0; @@ -180,8 +199,8 @@ int main(int argc, char *argv[]) // Initialize errbuf memset(errbuf, 0, sizeof(errbuf)); - strncpy(address, RPCAP_DEFAULT_NETADDR, MAX_LINE); - strncpy(port, RPCAP_DEFAULT_NETPORT, MAX_LINE); + pcap_strlcpy(address, RPCAP_DEFAULT_NETADDR, sizeof (address)); + pcap_strlcpy(port, RPCAP_DEFAULT_NETPORT, sizeof (port)); // Prepare to open a new server socket memset(&mainhints, 0, sizeof(struct addrinfo)); @@ -191,7 +210,15 @@ int main(int argc, char *argv[]) mainhints.ai_socktype = SOCK_STREAM; // Getting the proper command line options - while ((retval = getopt(argc, argv, "b:dDhip:4l:na:s:f:v")) != -1) +# ifdef HAVE_OPENSSL +# define SSL_CLOPTS "SK:X:C" +# else +# define SSL_CLOPTS "" +# endif + +# define CLOPTS "b:dDhip:4l:na:s:f:v" SSL_CLOPTS + + while ((retval = getopt(argc, argv, CLOPTS)) != -1) { switch (retval) { @@ -200,10 +227,10 @@ int main(int argc, char *argv[]) rpcapd_log_set(log_to_systemlog, log_debug_messages); break; case 'b': - strncpy(address, optarg, MAX_LINE); + pcap_strlcpy(address, optarg, sizeof (address)); break; case 'p': - strncpy(port, optarg, MAX_LINE); + pcap_strlcpy(port, optarg, sizeof (port)); break; case '4': mainhints.ai_family = PF_INET; // IPv4 server only @@ -215,7 +242,7 @@ int main(int argc, char *argv[]) break; case 'i': #ifdef _WIN32 - printusage(); + printusage(stderr); exit(1); #else isrunbyinetd = 1; @@ -231,7 +258,7 @@ int main(int argc, char *argv[]) break; case 'l': { - strncpy(hostlist, optarg, sizeof(hostlist)); + pcap_strlcpy(hostlist, optarg, sizeof(hostlist)); break; } case 'a': @@ -246,12 +273,12 @@ int main(int argc, char *argv[]) { tmpport = pcap_strtok_r(NULL, RPCAP_HOSTLIST_SEP, &lasts); - pcap_strlcpy(activelist[i].address, tmpaddress, MAX_LINE); + pcap_strlcpy(activelist[i].address, tmpaddress, sizeof (activelist[i].address)); if ((tmpport == NULL) || (strcmp(tmpport, "DEFAULT") == 0)) // the user choose a custom port - pcap_strlcpy(activelist[i].port, RPCAP_DEFAULT_NETPORT_ACTIVE, MAX_LINE); + pcap_strlcpy(activelist[i].port, RPCAP_DEFAULT_NETPORT_ACTIVE, sizeof (activelist[i].port)); else - pcap_strlcpy(activelist[i].port, tmpport, MAX_LINE); + pcap_strlcpy(activelist[i].port, tmpport, sizeof (activelist[i].port)); tmpaddress = pcap_strtok_r(NULL, RPCAP_HOSTLIST_SEP, &lasts); @@ -266,13 +293,27 @@ int main(int argc, char *argv[]) break; } case 'f': - pcap_strlcpy(loadfile, optarg, MAX_LINE); + pcap_strlcpy(loadfile, optarg, sizeof (loadfile)); break; case 's': - pcap_strlcpy(savefile, optarg, MAX_LINE); + pcap_strlcpy(savefile, optarg, sizeof (savefile)); break; +#ifdef HAVE_OPENSSL + case 'S': + uses_ssl = 1; + break; + case 'C': + enable_compression = 1; + break; + case 'K': + ssl_set_keyfile(optarg); + break; + case 'X': + ssl_set_certfile(optarg); + break; +#endif case 'h': - printusage(); + printusage(stdout); exit(0); /*NOTREACHED*/ default: @@ -289,6 +330,16 @@ int main(int argc, char *argv[]) } #endif + // + // We want UTF-8 error messages. + // + if (pcap_init(PCAP_CHAR_ENC_UTF_8, errbuf) == -1) + { + rpcapd_log(LOGPRIO_ERROR, "%s", errbuf); + exit(-1); + } + pcap_fmt_set_encoding(PCAP_CHAR_ENC_UTF_8); + if (sock_init(errbuf, PCAP_ERRBUF_SIZE) == -1) { rpcapd_log(LOGPRIO_ERROR, "%s", errbuf); @@ -302,7 +353,7 @@ int main(int argc, char *argv[]) if (loadfile[0]) fileconf_read(); -#ifdef WIN32 +#ifdef _WIN32 // // Create a handle to signal the main loop to tell it to do // something. @@ -310,8 +361,8 @@ int main(int argc, char *argv[]) state_change_event = CreateEvent(NULL, FALSE, FALSE, NULL); if (state_change_event == NULL) { - sock_geterror("Can't create state change event", errbuf, - PCAP_ERRBUF_SIZE); + sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE, + "Can't create state change event"); rpcapd_log(LOGPRIO_ERROR, "%s", errbuf); exit(2); } @@ -321,8 +372,8 @@ int main(int argc, char *argv[]) // if (!SetConsoleCtrlHandler(main_ctrl_event, TRUE)) { - sock_geterror("Can't set control handler", errbuf, - PCAP_ERRBUF_SIZE); + sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE, + "Can't set control handler"); rpcapd_log(LOGPRIO_ERROR, "%s", errbuf); exit(2); } @@ -342,6 +393,17 @@ int main(int argc, char *argv[]) signal(SIGPIPE, SIG_IGN); #endif +# ifdef HAVE_OPENSSL + if (uses_ssl) { + if (ssl_init_once(1, enable_compression, errbuf, PCAP_ERRBUF_SIZE) < 0) + { + rpcapd_log(LOGPRIO_ERROR, "Can't initialize SSL: %s", + errbuf); + exit(2); + } + } +# endif + #ifndef _WIN32 if (isrunbyinetd) { @@ -365,8 +427,8 @@ int main(int argc, char *argv[]) sockctrl = dup(0); if (sockctrl == -1) { - sock_geterror("Can't dup standard input", errbuf, - PCAP_ERRBUF_SIZE); + sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE, + "Can't dup standard input"); rpcapd_log(LOGPRIO_ERROR, "%s", errbuf); exit(2); } @@ -399,7 +461,7 @@ int main(int argc, char *argv[]) exit(0); } (void)daemon_serviceloop(sockctrl, 0, hostlist_copy, - nullAuthAllowed); + nullAuthAllowed, uses_ssl); // // Nothing more to do. @@ -442,7 +504,7 @@ int main(int argc, char *argv[]) // LINUX WARNING: the current linux implementation of pthreads requires a management thread // to handle some hidden stuff. So, as soon as you create the first thread, two threads are - // created. Fom this point on, the number of threads active are always one more compared + // created. From this point on, the number of threads active are always one more compared // to the number you're expecting // Second child continues @@ -454,7 +516,7 @@ int main(int argc, char *argv[]) // // If this call succeeds, it is blocking on Win32 // - if (svc_start() != 1) + if (!svc_start()) rpcapd_log(LOGPRIO_DEBUG, "Unable to start the service"); // When the previous call returns, the entire application has to be stopped. @@ -561,7 +623,7 @@ void main_startup(void) SOCKET sock; struct listen_sock *sock_info; - if ((sock = sock_open(tempaddrinfo, SOCKOPEN_SERVER, SOCKET_MAXCONN, errbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET) + if ((sock = sock_open(NULL, tempaddrinfo, SOCKOPEN_SERVER, SOCKET_MAXCONN, errbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET) { switch (tempaddrinfo->ai_family) { @@ -674,8 +736,8 @@ send_state_change_event(void) if (!SetEvent(state_change_event)) { - sock_geterror("SetEvent on shutdown event failed", errbuf, - PCAP_ERRBUF_SIZE); + sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE, + "SetEvent on shutdown event failed"); rpcapd_log(LOGPRIO_ERROR, "%s", errbuf); } } @@ -842,15 +904,15 @@ accept_connections(void) event = WSACreateEvent(); if (event == WSA_INVALID_EVENT) { - sock_geterror("Can't create socket event", errbuf, - PCAP_ERRBUF_SIZE); + sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE, + "Can't create socket event"); rpcapd_log(LOGPRIO_ERROR, "%s", errbuf); exit(2); } if (WSAEventSelect(sock_info->sock, event, FD_ACCEPT) == SOCKET_ERROR) { - sock_geterror("Can't setup socket event", errbuf, - PCAP_ERRBUF_SIZE); + sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE, + "Can't setup socket event"); rpcapd_log(LOGPRIO_ERROR, "%s", errbuf); exit(2); } @@ -868,8 +930,8 @@ accept_connections(void) WSA_INFINITE, FALSE); if (ret == WSA_WAIT_FAILED) { - sock_geterror("WSAWaitForMultipleEvents failed", errbuf, - PCAP_ERRBUF_SIZE); + sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE, + "WSAWaitForMultipleEvents failed"); rpcapd_log(LOGPRIO_ERROR, "%s", errbuf); exit(2); } @@ -908,8 +970,8 @@ accept_connections(void) if (WSAEnumNetworkEvents(sock_info->sock, events[i], &network_events) == SOCKET_ERROR) { - sock_geterror("WSAEnumNetworkEvents failed", - errbuf, PCAP_ERRBUF_SIZE); + sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE, + "WSAEnumNetworkEvents failed"); rpcapd_log(LOGPRIO_ERROR, "%s", errbuf); exit(2); } @@ -918,15 +980,15 @@ accept_connections(void) // // Did an error occur? // - if (network_events.iErrorCode[FD_ACCEPT_BIT] != 0) - { + if (network_events.iErrorCode[FD_ACCEPT_BIT] != 0) + { // // Yes - report it and keep going. // - sock_fmterror("Socket error", + sock_fmterrmsg(errbuf, + PCAP_ERRBUF_SIZE, network_events.iErrorCode[FD_ACCEPT_BIT], - errbuf, - PCAP_ERRBUF_SIZE); + "Socket error"); rpcapd_log(LOGPRIO_ERROR, "%s", errbuf); continue; } @@ -1100,7 +1162,7 @@ accept_connection(SOCKET listen_sock) break; } - // The accept() call can return this error when a signal is catched + // The accept() call can return this error when a signal is caught // In this case, we have simply to ignore this error code // Stevens, pg 124 #ifdef _WIN32 @@ -1112,7 +1174,7 @@ accept_connection(SOCKET listen_sock) // Don't check for errors here, since the error can be due to the fact that the thread // has been killed - sock_geterror("accept()", errbuf, PCAP_ERRBUF_SIZE); + sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE, "accept() failed"); rpcapd_log(LOGPRIO_ERROR, "Accept of control connection from client failed: %s", errbuf); return; @@ -1136,14 +1198,16 @@ accept_connection(SOCKET listen_sock) // if (WSAEventSelect(sockctrl, NULL, 0) == SOCKET_ERROR) { - sock_geterror("WSAEventSelect()", errbuf, PCAP_ERRBUF_SIZE); + sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE, + "WSAEventSelect() failed"); rpcapd_log(LOGPRIO_ERROR, "%s", errbuf); sock_close(sockctrl, NULL, 0); return; } if (ioctlsocket(sockctrl, FIONBIO, &off) == SOCKET_ERROR) { - sock_geterror("ioctlsocket(FIONBIO)", errbuf, PCAP_ERRBUF_SIZE); + sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE, + "ioctlsocket(FIONBIO) failed"); rpcapd_log(LOGPRIO_ERROR, "%s", errbuf); sock_close(sockctrl, NULL, 0); return; @@ -1236,7 +1300,7 @@ accept_connection(SOCKET listen_sock) exit(0); } (void)daemon_serviceloop(sockctrl, 0, hostlist_copy, - nullAuthAllowed); + nullAuthAllowed, uses_ssl); exit(0); } @@ -1296,13 +1360,15 @@ main_active(void *ptr) { int activeclose; - if ((sockctrl = sock_open(addrinfo, SOCKOPEN_CLIENT, 0, errbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET) + if ((sockctrl = sock_open(activepars->address, addrinfo, SOCKOPEN_CLIENT, 0, errbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET) { rpcapd_log(LOGPRIO_DEBUG, "%s", errbuf); - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error connecting to host %s, port %s, using protocol %s", + DIAG_OFF_FORMAT_TRUNCATION + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error connecting to host %s, port %s, using protocol %s", activepars->address, activepars->port, (hints.ai_family == AF_INET) ? "IPv4": (hints.ai_family == AF_INET6) ? "IPv6" : "Unspecified"); + DIAG_ON_FORMAT_TRUNCATION rpcapd_log(LOGPRIO_DEBUG, "%s", errbuf); @@ -1324,10 +1390,10 @@ main_active(void *ptr) // daemon_serviceloop() will free the copy. // activeclose = daemon_serviceloop(sockctrl, 1, - hostlist_copy, nullAuthAllowed); + hostlist_copy, nullAuthAllowed, uses_ssl); } - // If the connection is closed by the user explicitely, don't try to connect to it again + // If the connection is closed by the user explicitly, don't try to connect to it again // just exit the program if (activeclose == 1) break; @@ -1352,7 +1418,7 @@ unsigned __stdcall main_passive_serviceloop_thread(void *ptr) // told by the client to close. // (void)daemon_serviceloop(params.sockctrl, 0, params.hostlist, - nullAuthAllowed); + nullAuthAllowed, uses_ssl); return 0; } diff --git a/rpcapd/rpcapd.manadmin.in b/rpcapd/rpcapd.manadmin.in index 0a9d4e031a3c..791b4ad1735e 100644 --- a/rpcapd/rpcapd.manadmin.in +++ b/rpcapd/rpcapd.manadmin.in @@ -30,7 +30,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE .\" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.TH RPCAPD @MAN_ADMIN_COMMANDS@ "April 20, 2018" +.TH RPCAPD @MAN_ADMIN_COMMANDS@ "13 January 2019" .SH NAME rpcapd \- capture daemon to be controlled by a remote libpcap application .SH SYNOPSIS @@ -48,7 +48,6 @@ rpcapd .B \-l .I host_list ] -.br .ti +8 [ .B \-a @@ -62,17 +61,30 @@ rpcapd ] [ .B \-i ] -.br .ti +8 [ .B \-D ] [ .B \-s .I config_file -] [ +] +[ .B \-f .I config_file ] +[ +.B \-S +] +.ti +8 +[ +.B \-K +.I ssl_keyfile +] [ +.B \-X +.I ssl_certfile +] [ +.B \-C +] .br .ad .SH DESCRIPTION @@ -84,7 +96,9 @@ Rpcapd can run in two modes: passive mode (default) and active mode. .LP In passive mode, the client (e.g., a network sniffer) connects to .BR rpcapd . -It then sends hem the appropriate commands to start the capture. +The client then sends the appropriate commands to +.B rpcapd +to start the capture. .LP In active mode, .B rpcapd @@ -103,26 +117,34 @@ establishing the connection, the protocol continues its job in almost the same way in both active and passive mode. .SH Configuration file .LP -The user can create a configuration file in the same folder of the +The user can create a configuration file in the same directory as the executable, and put the configuration commands in there. In order for -rpcapd to execute the commands, you have to restart it on Win32, i.e. -the initialization file is parsed only at the beginning). The UNIX -version of rpcapd will reread the configuration file when receiving a -HUP signel. In that case, all the existing connections remain in place, +.B rpcapd +to execute the commands, it needs to be restarted on Win32, i.e. +the configuration file is parsed only at the beginning. The UNIX +version of +.B rpcapd +will reread the configuration file upon receiving a +HUP signal. In that case, all the existing connections remain in place, while the new connections will be created according to the new parameters. .LP In case a user does not want to create the configuration file manually, -they can launch rpcapd with the requested parameters plus "-s filename". +they can launch +.B rpcapd +with the desired flags plus +.BR "-s filename" . Rpcapd will parse all the parameters and save them into the specified configuration file. .SH Installing rpcapd on Win32 .LP The remote daemon is installed automatically when installing WinPcap. -The installation process places the rpcapd file into the WinPcap folder. +The installation process places the +.B rpcapd +executable file into the WinPcap folder. This file can be executed either from the command line, or as a service. For instance, the installation process updates the list of available services list and it creates a new item (Remote Packet Capture Protocol -v.0 (experimental) ). To avoid security problems, the service is +v.0 (experimental)). To avoid security problems, the service is inactive and it has to be started manually (control panel - administrative tools - services - start). .LP @@ -134,7 +156,9 @@ flag (in order to make it run as a service) and the flag. .SH Starting rpcapd on Win32 .LP -The rpcapd executable can be launched directly, i.e. it can run in the +The +.B rpcapd +executable can be launched directly, i.e. it can run in the foreground as well (not as a daemon/service). The procedure is quite simple: you have to invoke the executable from the command line with all the requested parameters except for the @@ -172,13 +196,13 @@ By default, .B rpcapd listens on both IPv4 and IPv6 addresses. .TP -.BI -l " host_list" +.BI \-l " host_list" Only allow hosts specified in the .I host_list argument to connect to this server. .I host_list is a list of host names or IP addresses, separated by commas. -We suggest that you use use host names rather than literal IP addresses +We suggest that you use host names rather than literal IP addresses in order to avoid problems with different address families. .TP .B \-n @@ -202,8 +226,8 @@ is specified, it accepts passive connections as well. .TP .B \-d -Run in daemon mode (UNIX only) or as a service (Win32 only) -Warning (Win32): this switch is provided automatically when +Run in daemon mode (UNIX only) or as a service (Win32 only). +Warning (Win32): this flag is specified automatically when the service is started from the control panel. .TP .B \-i @@ -222,12 +246,37 @@ in the format specified by Load the current configuration from .I config_file in the format specified by -.BR rpcapd-config (@MAN_FILE_FORMATS@); -all switches specified from the command line are ignored. +.BR rpcapd-config (@MAN_FILE_FORMATS@) +and ignore all flags specified on the command line. .TP .B \-h Print this help screen. +.LP +If +.B rpcapd +was compiled with SSL support, the following options are also +available: +.TP +.B \-S +Require that SSL be used on connections. +.TP +.B \-C +With SSL enabled, XXX - I'm not sure how *fetching* the list of +compression mechanisms does anything to compression. +.TP +.B \-S +.I ssl_keyfile +With SSL enabled, use +.I ssl_keyfile +as the SSL key file. +.TP +.B \-X +.I ssl_certfile +With SSL enabled, use +.I ssl_certfile +as the SSL certificate file. .br .ad .SH "SEE ALSO" -pcap(3PCAP), rpcapd-config(@MAN_FILE_FORMATS@) +.BR pcap (3PCAP), +.BR rpcapd-config (@MAN_FILE_FORMATS@) diff --git a/rpcapd/win32-svc.c b/rpcapd/win32-svc.c index 3a19910d77ab..49b6804bfd93 100644 --- a/rpcapd/win32-svc.c +++ b/rpcapd/win32-svc.c @@ -38,15 +38,18 @@ #include "fileconf.h" #include "log.h" +#include "win32-svc.h" // for Win32 service stuff + static SERVICE_STATUS_HANDLE service_status_handle; static SERVICE_STATUS service_status; static void WINAPI svc_main(DWORD argc, char **argv); +static void WINAPI svc_control_handler(DWORD Opcode); static void update_svc_status(DWORD state, DWORD progress_indicator); -int svc_start(void) +BOOL svc_start(void) { - int rc; + BOOL rc; SERVICE_TABLE_ENTRY ste[] = { { PROGRAM_NAME, svc_main }, @@ -65,7 +68,8 @@ int svc_start(void) return rc; // FALSE if this is not started as a service } -void WINAPI svc_control_handler(DWORD Opcode) +static void WINAPI +svc_control_handler(DWORD Opcode) { switch(Opcode) { @@ -130,7 +134,8 @@ void WINAPI svc_control_handler(DWORD Opcode) return; } -void WINAPI svc_main(DWORD argc, char **argv) +static void WINAPI +svc_main(DWORD argc, char **argv) { service_status_handle = RegisterServiceCtrlHandler(PROGRAM_NAME, svc_control_handler); diff --git a/rpcapd/win32-svc.h b/rpcapd/win32-svc.h index 3f511d2c5587..60b25ecff19e 100644 --- a/rpcapd/win32-svc.h +++ b/rpcapd/win32-svc.h @@ -30,4 +30,4 @@ * */ -int svc_start(void); +BOOL svc_start(void); diff --git a/savefile.c b/savefile.c index 5b1e14cf3c62..db8a3aa05972 100644 --- a/savefile.c +++ b/savefile.c @@ -54,22 +54,22 @@ #include "sf-pcap.h" #include "sf-pcapng.h" #include "pcap-common.h" +#include "charconv.h" #ifdef _WIN32 /* - * These aren't exported on Windows, because they would only work if both + * This isn't exported on Windows, because it would only work if both * WinPcap/Npcap and the code using it were to use the Universal CRT; otherwise, * a FILE structure in WinPcap/Npcap and a FILE structure in the code using it * could be different if they're using different versions of the C runtime. * - * Instead, pcap/pcap.h defines them as macros that wrap the hopen versions, - * with the wrappers calling _fileno() and _get_osfhandle() themselves, - * so that they convert the appropriate CRT version's FILE structure to + * Instead, pcap/pcap.h defines it as a macro that wraps the hopen version, + * with the wrapper calling _fileno() and _get_osfhandle() themselves, + * so that it convert the appropriate CRT version's FILE structure to * a HANDLE (which is OS-defined, not CRT-defined, and is part of the Win32 * and Win64 ABIs). */ static pcap_t *pcap_fopen_offline_with_tstamp_precision(FILE *, u_int, char *); -static pcap_t *pcap_fopen_offline(FILE *, char *); #endif /* @@ -106,48 +106,58 @@ sf_setnonblock(pcap_t *p, int nonblock _U_) * as it would have to handle reading partial packets and * keeping the state of the read.) */ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Savefiles cannot be put into non-blocking mode"); return (-1); } +static int +sf_cant_set_rfmon(pcap_t *p _U_) +{ + /* + * This is a savefile, not a device on which you can capture, + * so never say it supports being put into monitor mode. + */ + return (0); +} + static int sf_stats(pcap_t *p, struct pcap_stat *ps _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Statistics aren't available from savefiles"); return (-1); } #ifdef _WIN32 static struct pcap_stat * -sf_stats_ex(pcap_t *p, int *size) +sf_stats_ex(pcap_t *p, int *size _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Statistics aren't available from savefiles"); return (NULL); } static int -sf_setbuff(pcap_t *p, int dim) +sf_setbuff(pcap_t *p, int dim _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "The kernel buffer size cannot be set while reading from a file"); return (-1); } static int -sf_setmode(pcap_t *p, int mode) +sf_setmode(pcap_t *p, int mode _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "impossible to set mode while reading from a file"); return (-1); } static int -sf_setmintocopy(pcap_t *p, int size) +sf_setmintocopy(pcap_t *p, int size _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "The mintocopy parameter cannot be set while reading from a file"); return (-1); } @@ -155,7 +165,7 @@ sf_setmintocopy(pcap_t *p, int size) static HANDLE sf_getevent(pcap_t *pcap) { - (void)pcap_snprintf(pcap->errbuf, sizeof(pcap->errbuf), + (void)snprintf(pcap->errbuf, sizeof(pcap->errbuf), "The read event cannot be retrieved while reading from a file"); return (INVALID_HANDLE_VALUE); } @@ -164,7 +174,7 @@ static int sf_oid_get_request(pcap_t *p, bpf_u_int32 oid _U_, void *data _U_, size_t *lenp _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "An OID get request cannot be performed on a file"); return (PCAP_ERROR); } @@ -173,13 +183,13 @@ static int sf_oid_set_request(pcap_t *p, bpf_u_int32 oid _U_, const void *data _U_, size_t *lenp _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "An OID set request cannot be performed on a file"); return (PCAP_ERROR); } static u_int -sf_sendqueue_transmit(pcap_t *p, pcap_send_queue *queue, int sync) +sf_sendqueue_transmit(pcap_t *p, pcap_send_queue *queue _U_, int sync _U_) { pcap_strlcpy(p->errbuf, "Sending packets isn't supported on savefiles", PCAP_ERRBUF_SIZE); @@ -187,38 +197,38 @@ sf_sendqueue_transmit(pcap_t *p, pcap_send_queue *queue, int sync) } static int -sf_setuserbuffer(pcap_t *p, int size) +sf_setuserbuffer(pcap_t *p, int size _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "The user buffer cannot be set when reading from a file"); return (-1); } static int -sf_live_dump(pcap_t *p, char *filename, int maxsize, int maxpacks) +sf_live_dump(pcap_t *p, char *filename _U_, int maxsize _U_, int maxpacks _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Live packet dumping cannot be performed when reading from a file"); return (-1); } static int -sf_live_dump_ended(pcap_t *p, int sync) +sf_live_dump_ended(pcap_t *p, int sync _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Live packet dumping cannot be performed on a pcap_open_dead pcap_t"); return (-1); } static PAirpcapHandle -sf_get_airpcap_handle(pcap_t *pcap) +sf_get_airpcap_handle(pcap_t *pcap _U_) { return (NULL); } #endif static int -sf_inject(pcap_t *p, const void *buf _U_, size_t size _U_) +sf_inject(pcap_t *p, const void *buf _U_, int size _U_) { pcap_strlcpy(p->errbuf, "Sending packets isn't supported on savefiles", PCAP_ERRBUF_SIZE); @@ -232,7 +242,7 @@ sf_inject(pcap_t *p, const void *buf _U_, size_t size _U_) static int sf_setdirection(pcap_t *p, pcap_direction_t d _U_) { - pcap_snprintf(p->errbuf, sizeof(p->errbuf), + snprintf(p->errbuf, sizeof(p->errbuf), "Setting direction is not supported on savefiles"); return (-1); } @@ -247,6 +257,94 @@ sf_cleanup(pcap_t *p) pcap_freecode(&p->fcode); } +#ifdef _WIN32 +/* + * Wrapper for fopen() and _wfopen(). + * + * If we're in UTF-8 mode, map the pathname from UTF-8 to UTF-16LE and + * call _wfopen(). + * + * If we're not, just use fopen(); that'll treat it as being in the + * local code page. + */ +FILE * +charset_fopen(const char *path, const char *mode) +{ + wchar_t *utf16_path; +#define MAX_MODE_LEN 16 + wchar_t utf16_mode[MAX_MODE_LEN+1]; + int i; + char c; + FILE *fp; + int save_errno; + + if (pcap_utf_8_mode) { + /* + * Map from UTF-8 to UTF-16LE. + * Fail if there are invalid characters in the input + * string, rather than converting them to REPLACEMENT + * CHARACTER; the latter is appropriate for strings + * to be displayed to the user, but for file names + * you just want the attempt to open the file to fail. + */ + utf16_path = cp_to_utf_16le(CP_UTF8, path, + MB_ERR_INVALID_CHARS); + if (utf16_path == NULL) { + /* + * Error. Assume errno has been set. + * + * XXX - what about Windows errors? + */ + return (NULL); + } + + /* + * Now convert the mode to UTF-16LE as well. + * We assume the mode is ASCII, and that + * it's short, so that's easy. + */ + for (i = 0; (c = *mode) != '\0'; i++, mode++) { + if (c > 0x7F) { + /* Not an ASCII character; fail with EINVAL. */ + free(utf16_path); + errno = EINVAL; + return (NULL); + } + if (i >= MAX_MODE_LEN) { + /* The mode string is longer than we allow. */ + free(utf16_path); + errno = EINVAL; + return (NULL); + } + utf16_mode[i] = c; + } + utf16_mode[i] = '\0'; + + /* + * OK, we have UTF-16LE strings; hand them to + * _wfopen(). + */ + fp = _wfopen(utf16_path, utf16_mode); + + /* + * Make sure freeing the UTF-16LE string doesn't + * overwrite the error code we got from _wfopen(). + */ + save_errno = errno; + free(utf16_path); + errno = save_errno; + + return (fp); + } else { + /* + * This takes strings in the local code page as an + * argument. + */ + return (fopen(path, mode)); + } +} +#endif + pcap_t * pcap_open_offline_with_tstamp_precision(const char *fname, u_int precision, char *errbuf) @@ -255,13 +353,18 @@ pcap_open_offline_with_tstamp_precision(const char *fname, u_int precision, pcap_t *p; if (fname == NULL) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "A null pointer was supplied as the file name"); return (NULL); } if (fname[0] == '-' && fname[1] == '\0') { fp = stdin; + if (fp == NULL) { + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "The standard input is not open"); + return (NULL); + } #if defined(_WIN32) || defined(MSDOS) /* * We're reading from the standard input, so put it in binary @@ -272,12 +375,16 @@ pcap_open_offline_with_tstamp_precision(const char *fname, u_int precision, } else { /* + * Use charset_fopen(); on Windows, it tests whether we're + * in "local code page" or "UTF-8" mode, and treats the + * pathname appropriately, and on other platforms, it just + * wraps fopen(). + * * "b" is supported as of C90, so *all* UN*Xes should - * support it, even though it does nothing. It's - * required on Windows, as the file is a binary file - * and must be read in binary mode. + * support it, even though it does nothing. For MS-DOS, + * we again need it. */ - fp = fopen(fname, "rb"); + fp = charset_fopen(fname, "rb"); if (fp == NULL) { pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "%s", fname); @@ -381,6 +488,19 @@ pcap_fopen_offline_with_tstamp_precision(FILE *fp, u_int precision, u_int i; int err; + /* + * Fail if we were passed a NULL fp. + * + * That shouldn't happen if we're opening with a path name, but + * it could happen if buggy code is opening with a FILE * and + * didn't bother to make sure the FILE * isn't null. + */ + if (fp == NULL) { + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "Null FILE * pointer provided to savefile open routine"); + return (NULL); + } + /* * Read the first 4 bytes of the file; the network analyzer dump * file formats we support (pcap and pcapng), and several other @@ -394,8 +514,8 @@ pcap_fopen_offline_with_tstamp_precision(FILE *fp, u_int precision, pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "error reading dump file"); } else { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, - "truncated dump file; tried to read %" PRIsize " file header bytes, only got %" PRIsize, + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "truncated dump file; tried to read %zu file header bytes, only got %zu", sizeof(magic), amt_read); } return (NULL); @@ -421,7 +541,7 @@ pcap_fopen_offline_with_tstamp_precision(FILE *fp, u_int precision, /* * Well, who knows what this mess is.... */ - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "unknown file format"); + snprintf(errbuf, PCAP_ERRBUF_SIZE, "unknown file format"); return (NULL); found: @@ -441,6 +561,7 @@ found: p->selectable_fd = fileno(fp); #endif + p->can_set_rfmon_op = sf_cant_set_rfmon; p->read_op = pcap_offline_read; p->inject_op = sf_inject; p->setfilter_op = install_bpf_program; @@ -470,6 +591,11 @@ found: */ p->oneshot_callback = pcap_oneshot; + /* + * Default breakloop operation. + */ + p->breakloop_op = pcap_breakloop_common; + /* * Savefiles never require special BPF code generation. */ @@ -480,15 +606,19 @@ found: return (p); } -#ifdef _WIN32 -static -#endif +/* + * This isn't needed on Windows; we #define pcap_fopen_offline() as + * a wrapper around pcap_hopen_offline(), and we don't call it from + * inside this file, so it's unused. + */ +#ifndef _WIN32 pcap_t * pcap_fopen_offline(FILE *fp, char *errbuf) { return (pcap_fopen_offline_with_tstamp_precision(fp, PCAP_TSTAMP_PRECISION_MICRO, errbuf)); } +#endif /* * Read packets from a capture file, and call the callback for each @@ -499,12 +629,27 @@ int pcap_offline_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) { struct bpf_insn *fcode; - int status = 0; int n = 0; u_char *data; - while (status == 0) { + /* + * 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(cnt)) + cnt = INT_MAX; + + for (;;) { struct pcap_pkthdr h; + int status; /* * Has "pcap_breakloop()" been called? @@ -524,16 +669,28 @@ pcap_offline_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) } status = p->next_packet_op(p, &h, &data); - if (status) { - if (status == 1) - return (0); + if (status < 0) { + /* + * Error. Pass it back to the caller. + */ return (status); } + if (status == 0) { + /* + * EOF. Nothing more to process; + */ + break; + } + /* + * OK, we've read a packet; run it through the filter + * and, if it passes, process it. + */ if ((fcode = p->fcode.bf_insns) == NULL || - bpf_filter(fcode, data, h.len, h.caplen)) { + pcap_filter(fcode, data, h.len, h.caplen)) { (*callback)(user, &h, data); - if (++n >= cnt && cnt > 0) + n++; /* count the packet */ + if (n >= cnt) break; } } diff --git a/scanner.l b/scanner.l index effcf815d65e..85fe395aa55e 100644 --- a/scanner.l +++ b/scanner.l @@ -24,6 +24,13 @@ */ #include +/* + * grammar.h requires gencode.h and sometimes breaks in a polluted namespace + * (see ftmacros.h), so include it early. + */ +#include "gencode.h" +#include "grammar.h" + #include "diag-control.h" } @@ -85,15 +92,10 @@ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -#include #include #include "pcap-int.h" -#include "gencode.h" - -#include "grammar.h" - /* * Earlier versions of Flex don't declare these, so we declare them * ourselves to squelch warnings. @@ -147,8 +149,7 @@ void pcap_set_column(int, yyscan_t); #include "os-proto.h" #endif -static int stoi(char *); -static inline int xdtoi(int); +static int stou(char *, YYSTYPE *, compiler_state_t *); /* * Disable diagnostics in the code generated by Flex. @@ -339,6 +340,8 @@ len|length return LEN; inbound return INBOUND; outbound return OUTBOUND; +ifindex return IFINDEX; + vlan return VLAN; mpls return MPLS; pppoed return PPPOED; @@ -393,7 +396,7 @@ hsls return HSLS; ">>" return RSH; ${B} { yylval->s = sdup(yyextra, yytext); return AID; } {MAC} { yylval->s = sdup(yyextra, yytext); return EID; } -{N} { yylval->i = stoi((char *)yytext); return NUM; } +{N} { return stou(yytext, yylval, yyextra); } ({N}\.{N})|({N}\.{N}\.{N})|({N}\.{N}\.{N}\.{N}) { yylval->s = sdup(yyextra, (char *)yytext); return HID; } {V6} { @@ -416,62 +419,66 @@ ${B} { yylval->s = sdup(yyextra, yytext); return AID; } return HID6; } {B}:+({B}:+)+ { bpf_set_error(yyextra, "bogus ethernet address %s", yytext); yylval->s = NULL; return EID; } -icmptype { yylval->i = 0; return NUM; } -icmpcode { yylval->i = 1; return NUM; } -icmp-echoreply { yylval->i = 0; return NUM; } -icmp-unreach { yylval->i = 3; return NUM; } -icmp-sourcequench { yylval->i = 4; return NUM; } -icmp-redirect { yylval->i = 5; return NUM; } -icmp-echo { yylval->i = 8; return NUM; } -icmp-routeradvert { yylval->i = 9; return NUM; } -icmp-routersolicit { yylval->i = 10; return NUM; } -icmp-timxceed { yylval->i = 11; return NUM; } -icmp-paramprob { yylval->i = 12; return NUM; } -icmp-tstamp { yylval->i = 13; return NUM; } -icmp-tstampreply { yylval->i = 14; return NUM; } -icmp-ireq { yylval->i = 15; return NUM; } -icmp-ireqreply { yylval->i = 16; return NUM; } -icmp-maskreq { yylval->i = 17; return NUM; } -icmp-maskreply { yylval->i = 18; return NUM; } +icmptype { yylval->h = 0; return NUM; } +icmpcode { yylval->h = 1; return NUM; } +icmp-echoreply { yylval->h = 0; return NUM; } +icmp-unreach { yylval->h = 3; return NUM; } +icmp-sourcequench { yylval->h = 4; return NUM; } +icmp-redirect { yylval->h = 5; return NUM; } +icmp-echo { yylval->h = 8; return NUM; } +icmp-routeradvert { yylval->h = 9; return NUM; } +icmp-routersolicit { yylval->h = 10; return NUM; } +icmp-timxceed { yylval->h = 11; return NUM; } +icmp-paramprob { yylval->h = 12; return NUM; } +icmp-tstamp { yylval->h = 13; return NUM; } +icmp-tstampreply { yylval->h = 14; return NUM; } +icmp-ireq { yylval->h = 15; return NUM; } +icmp-ireqreply { yylval->h = 16; return NUM; } +icmp-maskreq { yylval->h = 17; return NUM; } +icmp-maskreply { yylval->h = 18; return NUM; } -icmp6type { yylval->i = 0; return NUM; } -icmp6code { yylval->i = 1; return NUM; } +icmp6type { yylval->h = 0; return NUM; } +icmp6code { yylval->h = 1; return NUM; } -icmp6-echo { yylval->i = 128; return NUM; } -icmp6-echoreply { yylval->i = 129; return NUM; } -icmp6-multicastlistenerquery { yylval->i = 130; return NUM; } -icmp6-multicastlistenerreportv1 { yylval->i = 131; return NUM; } -icmp6-multicastlistenerdone { yylval->i = 132; return NUM; } -icmp6-routersolicit { yylval->i = 133; return NUM; } -icmp6-routeradvert { yylval->i = 134; return NUM; } -icmp6-neighborsolicit { yylval->i = 135; return NUM; } -icmp6-neighboradvert { yylval->i = 136; return NUM; } -icmp6-redirect { yylval->i = 137; return NUM; } -icmp6-routerrenum { yylval->i = 138; return NUM; } -icmp6-nodeinformationquery { yylval->i = 139; return NUM; } -icmp6-nodeinformationresponse { yylval->i = 140; return NUM; } -icmp6-ineighbordiscoverysolicit { yylval->i = 141; return NUM; } -icmp6-ineighbordiscoveryadvert { yylval->i = 142; return NUM; } -icmp6-multicastlistenerreportv2 { yylval->i = 143; return NUM; } -icmp6-homeagentdiscoveryrequest { yylval->i = 144; return NUM; } -icmp6-homeagentdiscoveryreply { yylval->i = 145; return NUM; } -icmp6-mobileprefixsolicit { yylval->i = 146; return NUM; } -icmp6-mobileprefixadvert { yylval->i = 147; return NUM; } -icmp6-certpathsolicit { yylval->i = 148; return NUM; } -icmp6-certpathadvert { yylval->i = 149; return NUM; } -icmp6-multicastrouteradvert { yylval->i = 151; return NUM; } -icmp6-multicastroutersolicit { yylval->i = 152; return NUM; } -icmp6-multicastrouterterm { yylval->i = 153; return NUM; } +icmp6-destinationunreach { yylval->h = 1; return NUM; } +icmp6-packettoobig { yylval->h = 2; return NUM; } +icmp6-timeexceeded { yylval->h = 3; return NUM; } +icmp6-parameterproblem { yylval->h = 4; return NUM; } +icmp6-echo { yylval->h = 128; return NUM; } +icmp6-echoreply { yylval->h = 129; return NUM; } +icmp6-multicastlistenerquery { yylval->h = 130; return NUM; } +icmp6-multicastlistenerreportv1 { yylval->h = 131; return NUM; } +icmp6-multicastlistenerdone { yylval->h = 132; return NUM; } +icmp6-routersolicit { yylval->h = 133; return NUM; } +icmp6-routeradvert { yylval->h = 134; return NUM; } +icmp6-neighborsolicit { yylval->h = 135; return NUM; } +icmp6-neighboradvert { yylval->h = 136; return NUM; } +icmp6-redirect { yylval->h = 137; return NUM; } +icmp6-routerrenum { yylval->h = 138; return NUM; } +icmp6-nodeinformationquery { yylval->h = 139; return NUM; } +icmp6-nodeinformationresponse { yylval->h = 140; return NUM; } +icmp6-ineighbordiscoverysolicit { yylval->h = 141; return NUM; } +icmp6-ineighbordiscoveryadvert { yylval->h = 142; return NUM; } +icmp6-multicastlistenerreportv2 { yylval->h = 143; return NUM; } +icmp6-homeagentdiscoveryrequest { yylval->h = 144; return NUM; } +icmp6-homeagentdiscoveryreply { yylval->h = 145; return NUM; } +icmp6-mobileprefixsolicit { yylval->h = 146; return NUM; } +icmp6-mobileprefixadvert { yylval->h = 147; return NUM; } +icmp6-certpathsolicit { yylval->h = 148; return NUM; } +icmp6-certpathadvert { yylval->h = 149; return NUM; } +icmp6-multicastrouteradvert { yylval->h = 151; return NUM; } +icmp6-multicastroutersolicit { yylval->h = 152; return NUM; } +icmp6-multicastrouterterm { yylval->h = 153; return NUM; } -tcpflags { yylval->i = 13; return NUM; } -tcp-fin { yylval->i = 0x01; return NUM; } -tcp-syn { yylval->i = 0x02; return NUM; } -tcp-rst { yylval->i = 0x04; return NUM; } -tcp-push { yylval->i = 0x08; return NUM; } -tcp-ack { yylval->i = 0x10; return NUM; } -tcp-urg { yylval->i = 0x20; return NUM; } -tcp-ece { yylval->i = 0x40; return NUM; } -tcp-cwr { yylval->i = 0x80; return NUM; } +tcpflags { yylval->h = 13; return NUM; } +tcp-fin { yylval->h = 0x01; return NUM; } +tcp-syn { yylval->h = 0x02; return NUM; } +tcp-rst { yylval->h = 0x04; return NUM; } +tcp-push { yylval->h = 0x08; return NUM; } +tcp-ack { yylval->h = 0x10; return NUM; } +tcp-urg { yylval->h = 0x20; return NUM; } +tcp-ece { yylval->h = 0x40; return NUM; } +tcp-cwr { yylval->h = 0x80; return NUM; } [A-Za-z0-9]([-_.A-Za-z0-9]*[.A-Za-z0-9])? { yylval->s = sdup(yyextra, (char *)yytext); return ID; } "\\"[^ !()\n\t]+ { yylval->s = sdup(yyextra, (char *)yytext + 1); return ID; } @@ -483,40 +490,110 @@ tcp-cwr { yylval->i = 0x80; return NUM; } */ DIAG_ON_FLEX -/* Hex digit to integer. */ -static inline int -xdtoi(int c) -{ - if (isdigit(c)) - return c - '0'; - else if (islower(c)) - return c - 'a' + 10; - else - return c - 'A' + 10; -} - /* - * Convert string to integer. Just like atoi(), but checks for + * Convert string to 32-bit unsigned integer. Just like atoi(), but checks for * preceding 0x or 0 and uses hex or octal instead of decimal. + * + * On success, sets yylval->h to the value and returns NUM. + * On failure, sets the BPF error string and returns LEX_ERROR, to force + * the parse to stop. */ static int -stoi(char *s) +stou(char *yytext_arg, YYSTYPE *yylval_arg, compiler_state_t *yyextra_arg) { - int base = 10; - int n = 0; + bpf_u_int32 n = 0; + unsigned int digit; + char *s = yytext_arg; + /* + * yytext_arg is guaranteed either to be a string of decimal digits + * or 0[xX] followed by a string of hex digits. + */ if (*s == '0') { if (s[1] == 'x' || s[1] == 'X') { - s += 2; - base = 16; - } - else { - base = 8; + /* + * Begins with 0x or 0X, so hex. + * Guaranteed to be all hex digits following the + * prefix, so anything that's not 0-9 or a-f is + * A-F. + */ + s += 2; /* skip the prefix */ + while ((digit = *s++) != '\0') { + if (digit >= '0' && digit <= '9') + digit = digit - '0'; + else if (digit >= 'a' && digit <= 'f') + digit = digit - 'a' + 10; + else + digit = digit - 'A' + 10; + + /* + * Check for overflow. + */ + if (n > 0xFFFFFFFU) { + /* + * We have more than 28 bits of + * number, and are about to + * add 4 more; that won't fit + * in 32 bits. + */ + bpf_set_error(yyextra_arg, + "number %s overflows 32 bits", + yytext_arg); + return LEX_ERROR; + } + n = (n << 4) + digit; + } + } else { + /* + * Begins with 0, but not 0x or 0X, so octal. + * Guaranteed to be all *decimal* digits following + * the prefix, so we need to catch 8 and 9 and + * report an error. + */ s += 1; + while ((digit = *s++) != '\0') { + if (digit >= '0' && digit <= '7') + digit = digit - '0'; + else { + bpf_set_error(yyextra_arg, + "number %s contains non-octal digit", + yytext_arg); + return LEX_ERROR; + } + if (n > 03777777777U) { + /* + * We have more than 29 bits of + * number, and are about to add + * 3 more; that won't fit in + * 32 bits. + */ + bpf_set_error(yyextra_arg, + "number %s overflows 32 bits", + yytext_arg); + return LEX_ERROR; + } + n = (n << 3) + digit; + } + } + } else { + /* + * Decimal. + */ + while ((digit = *s++) != '\0') { + digit = digit - '0'; +#define CUTOFF_DEC (0xFFFFFFFFU / 10U) +#define CUTLIM_DEC (0xFFFFFFFFU % 10U) + if (n > CUTOFF_DEC || + (n == CUTOFF_DEC && digit > CUTLIM_DEC)) { + bpf_set_error(yyextra_arg, + "number %s overflows 32 bits", + yytext_arg); + return LEX_ERROR; + } + n = (n * 10) + digit; } } - while (*s) - n = n * base + xdtoi(*s++); - return n; + yylval_arg->h = n; + return NUM; } diff --git a/sf-pcap.c b/sf-pcap.c index 60c73a899c27..4294933129dc 100644 --- a/sf-pcap.c +++ b/sf-pcap.c @@ -46,6 +46,7 @@ #include /* for INT_MAX */ #include "pcap-int.h" +#include "pcap-util.h" #include "pcap-common.h" @@ -70,6 +71,10 @@ /* * Standard libpcap format. + * + * The same value is used in the rpcap protocol as an indication of + * the server byte order, to let the client know whether it needs to + * byte-swap some host-byte-order metadata. */ #define TCPDUMP_MAGIC 0xa1b2c3d4 @@ -194,8 +199,8 @@ pcap_check_header(const uint8_t *magic, FILE *fp, u_int precision, char *errbuf, pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "error reading dump file"); } else { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, - "truncated dump file; tried to read %" PRIsize " file header bytes, only got %" PRIsize, + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "truncated dump file; tried to read %zu file header bytes, only got %zu", sizeof(hdr), amt_read); } *err = 1; @@ -215,7 +220,7 @@ pcap_check_header(const uint8_t *magic, FILE *fp, u_int precision, char *errbuf, } if (hdr.version_major < PCAP_VERSION_MAJOR) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "archaic pcap savefile format"); *err = 1; return (NULL); @@ -229,7 +234,7 @@ pcap_check_header(const uint8_t *magic, FILE *fp, u_int precision, char *errbuf, hdr.version_minor <= PCAP_VERSION_MINOR) || (hdr.version_major == 543 && hdr.version_minor == 0))) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "unsupported pcap savefile version %u.%u", hdr.version_major, hdr.version_minor); *err = 1; @@ -240,7 +245,7 @@ pcap_check_header(const uint8_t *magic, FILE *fp, u_int precision, char *errbuf, * OK, this is a good pcap file. * Allocate a pcap_t for it. */ - p = pcap_open_offline_common(errbuf, sizeof (struct pcap_sf)); + p = PCAP_OPEN_OFFLINE_COMMON(errbuf, struct pcap_sf); if (p == NULL) { /* Allocation failed. */ *err = 1; @@ -249,7 +254,6 @@ pcap_check_header(const uint8_t *magic, FILE *fp, u_int precision, char *errbuf, p->swapped = swapped; p->version_major = hdr.version_major; p->version_minor = hdr.version_minor; - p->tzoff = hdr.thiszone; p->linktype = linktype_to_dlt(LT_LINKTYPE(hdr.linktype)); p->linktype_ext = LT_LINKTYPE_EXT(hdr.linktype); p->snapshot = pcap_adjust_snapshot(p->linktype, hdr.snaplen); @@ -292,7 +296,7 @@ pcap_check_header(const uint8_t *magic, FILE *fp, u_int precision, char *errbuf, ps->scale_type = PASS_THROUGH; } else { /* - * The file has microoseconds, the user + * The file has microseconds, the user * wants nanoseconds; scale the * precision up. */ @@ -301,7 +305,7 @@ pcap_check_header(const uint8_t *magic, FILE *fp, u_int precision, char *errbuf, break; default: - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "unknown time stamp resolution %u", precision); free(p); *err = 1; @@ -404,7 +408,7 @@ pcap_check_header(const uint8_t *magic, FILE *fp, u_int precision, char *errbuf, p->bufsize = 2048; p->buffer = malloc(p->bufsize); if (p->buffer == NULL) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "out of memory"); + snprintf(errbuf, PCAP_ERRBUF_SIZE, "out of memory"); free(p); *err = 1; return (NULL); @@ -425,7 +429,7 @@ grow_buffer(pcap_t *p, u_int bufsize) bigger_buffer = realloc(p->buffer, bufsize); if (bigger_buffer == NULL) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "out of memory"); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "out of memory"); return (0); } p->buffer = bigger_buffer; @@ -435,7 +439,7 @@ grow_buffer(pcap_t *p, u_int bufsize) /* * Read and return the next packet from the savefile. Return the header - * in hdr and a pointer to the contents in data. Return 0 on success, 1 + * in hdr and a pointer to the contents in data. Return 1 on success, 0 * if there were no more packets, and -1 on an error. */ static int @@ -462,13 +466,13 @@ pcap_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) return (-1); } else { if (amt_read != 0) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "truncated dump file; tried to read %" PRIsize " header bytes, only got %" PRIsize, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "truncated dump file; tried to read %zu header bytes, only got %zu", ps->hdrsize, amt_read); return (-1); } /* EOF */ - return (1); + return (0); } } @@ -546,11 +550,11 @@ pcap_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) * below.) */ if (hdr->caplen > (bpf_u_int32)p->snapshot) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "invalid packet capture length %u, bigger than " "snaplen of %d", hdr->caplen, p->snapshot); } else { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "invalid packet capture length %u, bigger than " "maximum of %u", hdr->caplen, max_snaplen_for_dlt(p->linktype)); @@ -580,7 +584,7 @@ pcap_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) * userland. * * However, perhaps some versions of libpcap failed to - * set the snapshot length currectly in the file header + * set the snapshot length correctly in the file header * or the per-packet header, or perhaps this is a * corrupted safefile or a savefile built/modified by a * fuzz tester, so we check anyway. We grow the buffer @@ -622,8 +626,8 @@ pcap_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) * that would fail because we got EOF before * the read finished. */ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "truncated dump file; tried to read %u captured bytes, only got %" PRIsize, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "truncated dump file; tried to read %d captured bytes, only got %zu", p->snapshot, amt_read); } return (-1); @@ -646,8 +650,8 @@ pcap_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) PCAP_ERRBUF_SIZE, errno, "error reading dump file"); } else { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "truncated dump file; tried to read %u captured bytes, only got %" PRIsize, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "truncated dump file; tried to read %u captured bytes, only got %zu", hdr->caplen, bytes_read); } return (-1); @@ -673,7 +677,7 @@ pcap_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) new_bufsize = hdr->caplen; /* - * http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 + * https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 */ new_bufsize--; new_bufsize |= new_bufsize >> 1; @@ -698,8 +702,8 @@ pcap_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) PCAP_ERRBUF_SIZE, errno, "error reading dump file"); } else { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "truncated dump file; tried to read %u captured bytes, only got %" PRIsize, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "truncated dump file; tried to read %u captured bytes, only got %zu", hdr->caplen, amt_read); } return (-1); @@ -707,14 +711,13 @@ pcap_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) } *data = p->buffer; - if (p->swapped) - swap_pseudo_headers(p->linktype, hdr, *data); + pcap_post_process(p->linktype, p->swapped, hdr, *data); - return (0); + return (1); } static int -sf_write_header(pcap_t *p, FILE *fp, int linktype, int thiszone, int snaplen) +sf_write_header(pcap_t *p, FILE *fp, int linktype, int snaplen) { struct pcap_file_header hdr; @@ -722,9 +725,15 @@ sf_write_header(pcap_t *p, FILE *fp, int linktype, int thiszone, int snaplen) hdr.version_major = PCAP_VERSION_MAJOR; hdr.version_minor = PCAP_VERSION_MINOR; - hdr.thiszone = thiszone; - hdr.snaplen = snaplen; + /* + * https://www.tcpdump.org/manpages/pcap-savefile.5.txt states: + * thiszone: 4-byte time zone offset; this is always 0. + * sigfigs: 4-byte number giving the accuracy of time stamps + * in the file; this is always 0. + */ + hdr.thiszone = 0; hdr.sigfigs = 0; + hdr.snaplen = snaplen; hdr.linktype = linktype; if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1) @@ -743,13 +752,43 @@ pcap_dump(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) struct pcap_sf_pkthdr sf_hdr; f = (FILE *)user; - sf_hdr.ts.tv_sec = h->ts.tv_sec; - sf_hdr.ts.tv_usec = h->ts.tv_usec; + /* + * If the output file handle is in an error state, don't write + * anything. + * + * While in principle a file handle can return from an error state + * to a normal state (for example if a disk that is full has space + * freed), we have possibly left a broken file already, and won't + * be able to clean it up. The safest option is to do nothing. + * + * Note that if we could guarantee that fwrite() was atomic we + * might be able to insure that we don't produce a corrupted file, + * but the standard defines fwrite() as a series of fputc() calls, + * so we really have no insurance that things are not fubared. + * + * http://pubs.opengroup.org/onlinepubs/009695399/functions/fwrite.html + */ + if (ferror(f)) + return; + /* + * Better not try writing pcap files after + * 2038-01-19 03:14:07 UTC; switch to pcapng. + */ + sf_hdr.ts.tv_sec = (bpf_int32)h->ts.tv_sec; + sf_hdr.ts.tv_usec = (bpf_int32)h->ts.tv_usec; sf_hdr.caplen = h->caplen; sf_hdr.len = h->len; - /* XXX we should check the return status */ - (void)fwrite(&sf_hdr, sizeof(sf_hdr), 1, f); - (void)fwrite(sp, h->caplen, 1, f); + /* + * We only write the packet if we can write the header properly. + * + * This doesn't prevent us from having corrupted output, and if we + * for some reason don't get a complete write we don't have any + * way to set ferror() to prevent future writes from being + * attempted, but it is better than nothing. + */ + if (fwrite(&sf_hdr, sizeof(sf_hdr), 1, f) == 1) { + (void)fwrite(sp, h->caplen, 1, f); + } } static pcap_dumper_t * @@ -769,7 +808,7 @@ pcap_setup_dump(pcap_t *p, int linktype, FILE *f, const char *fname) else setvbuf(f, NULL, _IONBF, 0); #endif - if (sf_write_header(p, f, linktype, p->tzoff, p->snapshot) == -1) { + if (sf_write_header(p, f, linktype, p->snapshot) == -1) { pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, errno, "Can't write to %s", fname); if (f != stdout) @@ -793,14 +832,14 @@ pcap_dump_open(pcap_t *p, const char *fname) * link-layer type, so we can't use it. */ if (!p->activated) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: not-yet-activated pcap_t passed to pcap_dump_open", fname); return (NULL); } linktype = dlt_to_linktype(p->linktype); if (linktype == -1) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: link-layer type %d isn't supported in savefiles", fname, p->linktype); return (NULL); @@ -808,7 +847,7 @@ pcap_dump_open(pcap_t *p, const char *fname) linktype |= p->linktype_ext; if (fname == NULL) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "A null pointer was supplied as the file name"); return NULL; } @@ -822,7 +861,7 @@ pcap_dump_open(pcap_t *p, const char *fname) * required on Windows, as the file is a binary file * and must be written in binary mode. */ - f = fopen(fname, "wb"); + f = charset_fopen(fname, "wb"); if (f == NULL) { pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, errno, "%s", fname); @@ -875,7 +914,7 @@ pcap_dump_fopen(pcap_t *p, FILE *f) linktype = dlt_to_linktype(p->linktype); if (linktype == -1) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "stream: link-layer type %d isn't supported in savefiles", p->linktype); return (NULL); @@ -895,14 +934,14 @@ pcap_dump_open_append(pcap_t *p, const char *fname) linktype = dlt_to_linktype(p->linktype); if (linktype == -1) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: link-layer type %d isn't supported in savefiles", fname, linktype); return (NULL); } if (fname == NULL) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "A null pointer was supplied as the file name"); return NULL; } @@ -922,7 +961,7 @@ pcap_dump_open_append(pcap_t *p, const char *fname) * even though it does nothing. It's required on Windows, as the * file is a binary file and must be read in binary mode. */ - f = fopen(fname, "ab+"); + f = charset_fopen(fname, "ab+"); if (f == NULL) { pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, errno, "%s", fname); @@ -955,7 +994,7 @@ pcap_dump_open_append(pcap_t *p, const char *fname) (void)fclose(f); return (NULL); } else if (feof(f) && amt_read > 0) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: truncated pcap file header", fname); (void)fclose(f); return (NULL); @@ -991,7 +1030,7 @@ pcap_dump_open_append(pcap_t *p, const char *fname) case TCPDUMP_MAGIC: if (p->opt.tstamp_precision != PCAP_TSTAMP_PRECISION_MICRO) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: different time stamp precision, cannot append to file", fname); (void)fclose(f); return (NULL); @@ -1000,7 +1039,7 @@ pcap_dump_open_append(pcap_t *p, const char *fname) case NSEC_TCPDUMP_MAGIC: if (p->opt.tstamp_precision != PCAP_TSTAMP_PRECISION_NANO) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: different time stamp precision, cannot append to file", fname); (void)fclose(f); return (NULL); @@ -1009,7 +1048,7 @@ pcap_dump_open_append(pcap_t *p, const char *fname) case SWAPLONG(TCPDUMP_MAGIC): case SWAPLONG(NSEC_TCPDUMP_MAGIC): - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: different byte order, cannot append to file", fname); (void)fclose(f); return (NULL); @@ -1018,13 +1057,13 @@ pcap_dump_open_append(pcap_t *p, const char *fname) case SWAPLONG(KUZNETZOV_TCPDUMP_MAGIC): case NAVTEL_TCPDUMP_MAGIC: case SWAPLONG(NAVTEL_TCPDUMP_MAGIC): - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: not a pcap file to which we can append", fname); (void)fclose(f); return (NULL); default: - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: not a pcap file", fname); (void)fclose(f); return (NULL); @@ -1035,20 +1074,20 @@ pcap_dump_open_append(pcap_t *p, const char *fname) */ if (ph.version_major != PCAP_VERSION_MAJOR || ph.version_minor != PCAP_VERSION_MINOR) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: version is %u.%u, cannot append to file", fname, ph.version_major, ph.version_minor); (void)fclose(f); return (NULL); } if ((bpf_u_int32)linktype != ph.linktype) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: different linktype, cannot append to file", fname); (void)fclose(f); return (NULL); } if ((bpf_u_int32)p->snapshot != ph.snaplen) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: different snaplen, cannot append to file", fname); (void)fclose(f); return (NULL); @@ -1057,7 +1096,7 @@ pcap_dump_open_append(pcap_t *p, const char *fname) /* * A header isn't present; attempt to write it. */ - if (sf_write_header(p, f, linktype, p->tzoff, p->snapshot) == -1) { + if (sf_write_header(p, f, linktype, p->snapshot) == -1) { pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, errno, "Can't write to %s", fname); (void)fclose(f); diff --git a/sf-pcapng.c b/sf-pcapng.c index afaeb0566571..058a7244d62a 100644 --- a/sf-pcapng.c +++ b/sf-pcapng.c @@ -34,6 +34,7 @@ #include #include "pcap-int.h" +#include "pcap-util.h" #include "pcap-common.h" @@ -101,7 +102,8 @@ struct section_header_block { /* * Current version number. If major_version isn't PCAP_NG_VERSION_MAJOR, - * that means that this code can't read the file. + * or if minor_version isn't PCAP_NG_VERSION_MINOR or 2, that means that + * this code can't read the file. */ #define PCAP_NG_VERSION_MAJOR 1 #define PCAP_NG_VERSION_MINOR 0 @@ -197,6 +199,7 @@ typedef enum { * Per-interface information. */ struct pcap_ng_if { + uint32_t snaplen; /* snapshot length */ uint64_t tsresol; /* time stamp resolution */ tstamp_scale_type_t scale_type; /* how to scale */ uint64_t scale_factor; /* time stamp scale factor for power-of-10 tsresol */ @@ -265,8 +268,8 @@ read_bytes(FILE *fp, void *buf, size_t bytes_to_read, int fail_on_eof, } else { if (amt_read == 0 && !fail_on_eof) return (0); /* EOF */ - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, - "truncated pcapng dump file; tried to read %" PRIsize " bytes, only got %" PRIsize, + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "truncated pcapng dump file; tried to read %zu bytes, only got %zu", bytes_to_read, amt_read); } return (-1); @@ -301,8 +304,8 @@ read_block(FILE *fp, pcap_t *p, struct block_cursor *cursor, char *errbuf) */ if (bhdr.total_length < sizeof(struct block_header) + sizeof(struct block_trailer)) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, - "block in pcapng dump file has a length of %u < %" PRIsize, + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "block in pcapng dump file has a length of %u < %zu", bhdr.total_length, sizeof(struct block_header) + sizeof(struct block_trailer)); return (-1); @@ -315,8 +318,8 @@ read_block(FILE *fp, pcap_t *p, struct block_cursor *cursor, char *errbuf) /* * No. Report that as an error. */ - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, - "block in pcapng dump file has a length of %u that is not a multiple of 4" PRIsize, + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "block in pcapng dump file has a length of %u that is not a multiple of 4", bhdr.total_length); return (-1); } @@ -332,13 +335,13 @@ read_block(FILE *fp, pcap_t *p, struct block_cursor *cursor, char *errbuf) void *bigger_buffer; if (bhdr.total_length > ps->max_blocksize) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "pcapng block size %u > maximum %u", bhdr.total_length, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "pcapng block size %u > maximum %u", bhdr.total_length, ps->max_blocksize); return (-1); } bigger_buffer = realloc(p->buffer, bhdr.total_length); if (bigger_buffer == NULL) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "out of memory"); + snprintf(errbuf, PCAP_ERRBUF_SIZE, "out of memory"); return (-1); } p->buffer = bigger_buffer; @@ -369,7 +372,7 @@ read_block(FILE *fp, pcap_t *p, struct block_cursor *cursor, char *errbuf) /* * No. */ - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "block total length in header and trailer don't match"); return (-1); } @@ -394,7 +397,7 @@ get_from_block_data(struct block_cursor *cursor, size_t chunk_size, * the block data. */ if (cursor->data_remaining < chunk_size) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "block of type %u in pcapng dump file is too short", cursor->block_type); return (NULL); @@ -495,7 +498,7 @@ process_idb_options(pcap_t *p, struct block_cursor *cursor, uint64_t *tsresol, case OPT_ENDOFOPT: if (opthdr->option_length != 0) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Interface Description Block has opt_endofopt option with length %u != 0", opthdr->option_length); return (-1); @@ -504,13 +507,13 @@ process_idb_options(pcap_t *p, struct block_cursor *cursor, uint64_t *tsresol, case IF_TSRESOL: if (opthdr->option_length != 1) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Interface Description Block has if_tsresol option with length %u != 1", opthdr->option_length); return (-1); } if (saw_tsresol) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Interface Description Block has more than one if_tsresol option"); return (-1); } @@ -527,7 +530,7 @@ process_idb_options(pcap_t *p, struct block_cursor *cursor, uint64_t *tsresol, * Resolution is too high; 2^-{res} * won't fit in a 64-bit value. */ - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Interface Description Block if_tsresol option resolution 2^-%u is too high", tsresol_shift); return (-1); @@ -547,7 +550,7 @@ process_idb_options(pcap_t *p, struct block_cursor *cursor, uint64_t *tsresol, * the largest 64-bit unsigned * value is ~1.8*10^19). */ - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Interface Description Block if_tsresol option resolution 10^-%u is too high", tsresol_opt); return (-1); @@ -561,13 +564,13 @@ process_idb_options(pcap_t *p, struct block_cursor *cursor, uint64_t *tsresol, case IF_TSOFFSET: if (opthdr->option_length != 8) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Interface Description Block has if_tsoffset option with length %u != 8", opthdr->option_length); return (-1); } if (saw_tsoffset) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Interface Description Block has more than one if_tsoffset option"); return (-1); } @@ -587,7 +590,8 @@ done: } static int -add_interface(pcap_t *p, struct block_cursor *cursor, char *errbuf) +add_interface(pcap_t *p, struct interface_description_block *idbp, + struct block_cursor *cursor, char *errbuf) { struct pcap_ng_sf *ps; uint64_t tsresol; @@ -644,7 +648,7 @@ add_interface(pcap_t *p, struct block_cursor *cursor, char *errbuf) * possible 32-bit power of 2, as we do * size doubling. */ - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "more than %u interfaces in the file", 0x80000000U); return (0); @@ -675,7 +679,7 @@ add_interface(pcap_t *p, struct block_cursor *cursor, char *errbuf) * (unsigned) value divided by * sizeof (struct pcap_ng_if). */ - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "more than %u interfaces in the file", 0xFFFFFFFFU / ((u_int)sizeof (struct pcap_ng_if))); return (0); @@ -687,7 +691,7 @@ add_interface(pcap_t *p, struct block_cursor *cursor, char *errbuf) * We ran out of memory. * Give up. */ - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "out of memory for per-interface information (%u interfaces)", ps->ifcount); return (0); @@ -696,6 +700,8 @@ add_interface(pcap_t *p, struct block_cursor *cursor, char *errbuf) ps->ifaces = new_ifaces; } + ps->ifaces[ps->ifcount - 1].snaplen = idbp->snaplen; + /* * Set the default time stamp resolution and offset. */ @@ -858,8 +864,8 @@ pcap_ng_check_header(const uint8_t *magic, FILE *fp, u_int precision, */ if (total_length < sizeof(*bhdrp) + sizeof(*shbp) + sizeof(struct block_trailer) || (total_length > BT_SHB_INSANE_MAX)) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, - "Section Header Block in pcapng dump file has invalid length %" PRIsize " < _%u_ < %u (BT_SHB_INSANE_MAX)", + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "Section Header Block in pcapng dump file has invalid length %zu < _%u_ < %u (BT_SHB_INSANE_MAX)", sizeof(*bhdrp) + sizeof(*shbp) + sizeof(struct block_trailer), total_length, BT_SHB_INSANE_MAX); @@ -872,7 +878,7 @@ pcap_ng_check_header(const uint8_t *magic, FILE *fp, u_int precision, * OK, this is a good pcapng file. * Allocate a pcap_t for it. */ - p = pcap_open_offline_common(errbuf, sizeof (struct pcap_ng_sf)); + p = PCAP_OPEN_OFFLINE_COMMON(errbuf, struct pcap_ng_sf); if (p == NULL) { /* Allocation failed. */ *err = 1; @@ -895,7 +901,7 @@ pcap_ng_check_header(const uint8_t *magic, FILE *fp, u_int precision, break; default: - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "unknown time stamp resolution %u", precision); free(p); *err = 1; @@ -925,7 +931,7 @@ pcap_ng_check_header(const uint8_t *magic, FILE *fp, u_int precision, p->bufsize = total_length; p->buffer = malloc(p->bufsize); if (p->buffer == NULL) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "out of memory"); + snprintf(errbuf, PCAP_ERRBUF_SIZE, "out of memory"); free(p); *err = 1; return (NULL); @@ -958,10 +964,24 @@ pcap_ng_check_header(const uint8_t *magic, FILE *fp, u_int precision, * XXX - we don't care about the section length. */ } - /* currently only SHB version 1.0 is supported */ + /* Currently only SHB versions 1.0 and 1.2 are supported; + version 1.2 is treated as being the same as version 1.0. + See the current version of the pcapng specification. + + Version 1.2 is written by some programs that write additional + block types (which can be read by any code that handles them, + regardless of whether the minor version if 0 or 2, so that's + not a reason to change the minor version number). + + XXX - the pcapng specification says that readers should + just ignore sections with an unsupported version number; + presumably they can also report an error if they skip + all the way to the end of the file without finding + any versions that they support. */ if (! (shbp->major_version == PCAP_NG_VERSION_MAJOR && - shbp->minor_version == PCAP_NG_VERSION_MINOR)) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + (shbp->minor_version == PCAP_NG_VERSION_MINOR || + shbp->minor_version == 2))) { + snprintf(errbuf, PCAP_ERRBUF_SIZE, "unsupported pcapng savefile version %u.%u", shbp->major_version, shbp->minor_version); goto fail; @@ -984,7 +1004,7 @@ pcap_ng_check_header(const uint8_t *magic, FILE *fp, u_int precision, status = read_block(fp, p, &cursor, errbuf); if (status == 0) { /* EOF - no IDB in this file */ - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "the capture file has no Interface Description Blocks"); goto fail; } @@ -1013,7 +1033,7 @@ pcap_ng_check_header(const uint8_t *magic, FILE *fp, u_int precision, /* * Try to add this interface. */ - if (!add_interface(p, &cursor, errbuf)) + if (!add_interface(p, idbp, &cursor, errbuf)) goto fail; goto done; @@ -1026,7 +1046,7 @@ pcap_ng_check_header(const uint8_t *magic, FILE *fp, u_int precision, * not valid, as we don't know what link-layer * encapsulation the packet has. */ - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "the capture file has a packet block before any Interface Description Blocks"); goto fail; @@ -1039,7 +1059,6 @@ pcap_ng_check_header(const uint8_t *magic, FILE *fp, u_int precision, } done: - p->tzoff = 0; /* XXX - not used in pcap */ p->linktype = linktype_to_dlt(idbp->linktype); p->snapshot = pcap_adjust_snapshot(p->linktype, idbp->snaplen); p->linktype_ext = 0; @@ -1076,7 +1095,7 @@ pcap_ng_cleanup(pcap_t *p) /* * Read and return the next packet from the savefile. Return the header - * in hdr and a pointer to the contents in data. Return 0 on success, 1 + * in hdr and a pointer to the contents in data. Return 1 on success, 0 * if there were no more packets, and -1 on an error. */ static int @@ -1105,7 +1124,7 @@ pcap_ng_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) */ status = read_block(fp, p, &cursor, p->errbuf); if (status == 0) - return (1); /* EOF */ + return (0); /* EOF */ if (status == -1) return (-1); /* error */ switch (cursor.block_type) { @@ -1231,7 +1250,7 @@ pcap_ng_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) * interfaces? */ if (p->linktype != idbp->linktype) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "an interface has a type %u different from the type of the first interface", idbp->linktype); return (-1); @@ -1243,8 +1262,8 @@ pcap_ng_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) */ if ((bpf_u_int32)p->snapshot != pcap_adjust_snapshot(p->linktype, idbp->snaplen)) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "an interface has a snapshot length %u different from the type of the first interface", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "an interface has a snapshot length %u different from the snapshot length of the first interface", idbp->snaplen); return (-1); } @@ -1252,7 +1271,7 @@ pcap_ng_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) /* * Try to add this interface. */ - if (!add_interface(p, &cursor, p->errbuf)) + if (!add_interface(p, idbp, &cursor, p->errbuf)) return (-1); break; @@ -1295,7 +1314,7 @@ pcap_ng_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) /* * Byte order changes. */ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "the file has sections with different byte orders"); return (-1); @@ -1303,7 +1322,7 @@ pcap_ng_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) /* * Not a valid SHB. */ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "the file has a section with a bad byte order magic field"); return (-1); } @@ -1313,7 +1332,7 @@ pcap_ng_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) * we handle. */ if (shbp->major_version != PCAP_NG_VERSION_MAJOR) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "unknown pcapng savefile major version number %u", shbp->major_version); return (-1); @@ -1347,14 +1366,14 @@ found: /* * Yes. Fail. */ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "a packet arrived on interface %u, but there's no Interface Description Block for that interface", interface_id); return (-1); } if (hdr->caplen > (bpf_u_int32)p->snapshot) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "invalid packet capture length %u, bigger than " "snaplen of %d", hdr->caplen, p->snapshot); return (-1); @@ -1493,8 +1512,7 @@ found: if (*data == NULL) return (-1); - if (p->swapped) - swap_pseudo_headers(p->linktype, hdr, *data); + pcap_post_process(p->linktype, p->swapped, hdr, *data); - return (0); + return (1); } diff --git a/sockutils.c b/sockutils.c index d3e94649d9d4..933f32670761 100644 --- a/sockutils.c +++ b/sockutils.c @@ -56,11 +56,7 @@ #include /* for the errno variable */ #include /* for the stderr file */ #include /* for malloc() and free() */ -#ifdef HAVE_LIMITS_H -#include -#else -#define INT_MAX 2147483647 -#endif +#include /* for INT_MAX */ #include "pcap-int.h" @@ -71,7 +67,7 @@ /* * Winsock initialization. * - * Ask for WinSock 2.2. + * Ask for Winsock 2.2. */ #define WINSOCK_MAJOR_VERSION 2 #define WINSOCK_MINOR_VERSION 2 @@ -97,7 +93,7 @@ * * On Windows, send() and recv() return an int. * - * Wth MSVC, there *is* no ssize_t. + * With MSVC, there *is* no ssize_t. * * With MinGW, there is an ssize_t type; it is either an int (32 bit) * or a long long (64 bit). @@ -124,51 +120,187 @@ static int sock_ismcastaddr(const struct sockaddr *saddr); * * ****************************************************/ +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +const uint8_t *fuzzBuffer; +size_t fuzzSize; +size_t fuzzPos; + +void sock_initfuzz(const uint8_t *Data, size_t Size) { + fuzzPos = 0; + fuzzSize = Size; + fuzzBuffer = Data; +} + +static int fuzz_recv(char *bufp, int remaining) { + if (remaining > fuzzSize - fuzzPos) { + remaining = fuzzSize - fuzzPos; + } + if (fuzzPos < fuzzSize) { + memcpy(bufp, fuzzBuffer + fuzzPos, remaining); + } + fuzzPos += remaining; + return remaining; +} +#endif + +int sock_geterrcode(void) +{ +#ifdef _WIN32 + return GetLastError(); +#else + return errno; +#endif +} + /* - * Format an error message given an errno value (UN*X) or a WinSock error + * Format an error message given an errno value (UN*X) or a Winsock error * (Windows). */ -void sock_fmterror(const char *caller, int errcode, char *errbuf, int errbuflen) +void sock_vfmterrmsg(char *errbuf, size_t errbuflen, int errcode, + const char *fmt, va_list ap) { if (errbuf == NULL) return; #ifdef _WIN32 - pcap_fmt_errmsg_for_win32_err(errbuf, errbuflen, errcode, - "%s", caller); + pcap_vfmt_errmsg_for_win32_err(errbuf, errbuflen, errcode, + fmt, ap); #else - pcap_fmt_errmsg_for_errno(errbuf, errbuflen, errcode, - "%s", caller); + pcap_vfmt_errmsg_for_errno(errbuf, errbuflen, errcode, + fmt, ap); #endif } -/* - * \brief It retrieves the error message after an error occurred in the socket interface. - * - * This function is defined because of the different way errors are returned in UNIX - * and Win32. This function provides a consistent way to retrieve the error message - * (after a socket error occurred) on all the platforms. - * - * \param caller: a pointer to a user-allocated string which contains a message that has - * to be printed *before* the true error message. It could be, for example, 'this error - * comes from the recv() call at line 31'. - * - * \param errbuf: a pointer to an user-allocated buffer that will contain the complete - * error message. This buffer has to be at least 'errbuflen' in length. - * It can be NULL; in this case the error cannot be printed. - * - * \param errbuflen: length of the buffer that will contains the error. The error message cannot be - * larger than 'errbuflen - 1' because the last char is reserved for the string terminator. - * - * \return No return values. The error message is returned in the 'string' parameter. - */ -void sock_geterror(const char *caller, char *errbuf, int errbuflen) +void sock_fmterrmsg(char *errbuf, size_t errbuflen, int errcode, + const char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + sock_vfmterrmsg(errbuf, errbuflen, errcode, fmt, ap); + va_end(ap); +} + +/* + * Format an error message for the last socket error. + */ +void sock_geterrmsg(char *errbuf, size_t errbuflen, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + sock_vfmterrmsg(errbuf, errbuflen, sock_geterrcode(), fmt, ap); + va_end(ap); +} + +/* + * Types of error. + * + * These are sorted by how likely they are to be the "underlying" problem, + * so that lower-rated errors for a given address in a given family + * should not overwrite higher-rated errors for another address in that + * family, and higher-rated errors should overwrit elower-rated errors. + */ +typedef enum { + SOCK_CONNERR, /* connection error */ + SOCK_HOSTERR, /* host error */ + SOCK_NETERR, /* network error */ + SOCK_AFNOTSUPERR, /* address family not supported */ + SOCK_UNKNOWNERR, /* unknown error */ + SOCK_NOERR /* no error */ +} sock_errtype; + +static sock_errtype sock_geterrtype(int errcode) +{ + switch (errcode) { + #ifdef _WIN32 - sock_fmterror(caller, GetLastError(), errbuf, errbuflen); + case WSAECONNRESET: + case WSAECONNABORTED: + case WSAECONNREFUSED: #else - sock_fmterror(caller, errno, errbuf, errbuflen); + case ECONNRESET: + case ECONNABORTED: + case ECONNREFUSED: #endif + /* + * Connection error; this means the problem is probably + * that there's no server set up on the remote machine, + * or that it is set up, but it's IPv4-only or IPv6-only + * and we're trying the wrong address family. + * + * These overwrite all other errors, as they indicate + * that, even if somethng else went wrong in another + * attempt, this probably wouldn't work even if the + * other problems were fixed. + */ + return (SOCK_CONNERR); + +#ifdef _WIN32 + case WSAENETUNREACH: + case WSAETIMEDOUT: + case WSAEHOSTDOWN: + case WSAEHOSTUNREACH: +#else + case ENETUNREACH: + case ETIMEDOUT: + case EHOSTDOWN: + case EHOSTUNREACH: +#endif + /* + * Network errors that could be IPv4-specific, IPv6- + * specific, or present with both. + * + * Don't overwrite connection errors, but overwrite + * everything else. + */ + return (SOCK_HOSTERR); + +#ifdef _WIN32 + case WSAENETDOWN: + case WSAENETRESET: +#else + case ENETDOWN: + case ENETRESET: +#endif + /* + * Network error; this means we don't know whether + * there's a server set up on the remote machine, + * and we don't have a reason to believe that IPv6 + * any worse or better than IPv4. + * + * These probably indicate a local failure, e.g. + * an interface is down. + * + * Don't overwrite connection errors or host errors, + * but overwrite everything else. + */ + return (SOCK_NETERR); + +#ifdef _WIN32 + case WSAEAFNOSUPPORT: +#else + case EAFNOSUPPORT: +#endif + /* + * "Address family not supported" probably means + * "No soup^WIPv6 for you!". + * + * Don't overwrite connection errors, host errors, or + * network errors (none of which we should get for this + * address family if it's not supported), but overwrite + * everything else. + */ + return (SOCK_AFNOTSUPERR); + + default: + /* + * Anything else. + * + * Don't overwrite any errors. + */ + return (SOCK_UNKNOWNERR); + } } /* @@ -201,7 +333,7 @@ int sock_init(char *errbuf, int errbuflen) WINSOCK_MINOR_VERSION), &wsaData) != 0) { if (errbuf) - pcap_snprintf(errbuf, errbuflen, "Failed to initialize Winsock\n"); + snprintf(errbuf, errbuflen, "Failed to initialize Winsock\n"); WSACleanup(); @@ -262,6 +394,79 @@ static int sock_ismcastaddr(const struct sockaddr *saddr) } } +struct addr_status { + struct addrinfo *info; + int errcode; + sock_errtype errtype; +}; + +/* + * Sort by IPv4 address vs. IPv6 address. + */ +static int compare_addrs_to_try_by_address_family(const void *a, const void *b) +{ + const struct addr_status *addr_a = (const struct addr_status *)a; + const struct addr_status *addr_b = (const struct addr_status *)b; + + return addr_a->info->ai_family - addr_b->info->ai_family; +} + +/* + * Sort by error type and, within a given error type, by error code and, + * within a given error code, by IPv4 address vs. IPv6 address. + */ +static int compare_addrs_to_try_by_status(const void *a, const void *b) +{ + const struct addr_status *addr_a = (const struct addr_status *)a; + const struct addr_status *addr_b = (const struct addr_status *)b; + + if (addr_a->errtype == addr_b->errtype) + { + if (addr_a->errcode == addr_b->errcode) + { + return addr_a->info->ai_family - addr_b->info->ai_family; + } + return addr_a->errcode - addr_b->errcode; + } + + return addr_a->errtype - addr_b->errtype; +} + +static SOCKET sock_create_socket(struct addrinfo *addrinfo, char *errbuf, + int errbuflen) +{ + SOCKET sock; +#ifdef SO_NOSIGPIPE + int on = 1; +#endif + + sock = socket(addrinfo->ai_family, addrinfo->ai_socktype, + addrinfo->ai_protocol); + if (sock == INVALID_SOCKET) + { + sock_geterrmsg(errbuf, errbuflen, "socket() failed"); + return INVALID_SOCKET; + } + + /* + * Disable SIGPIPE, if we have SO_NOSIGPIPE. We don't want to + * have to deal with signals if the peer closes the connection, + * especially in client programs, which may not even be aware that + * they're sending to sockets. + */ +#ifdef SO_NOSIGPIPE + if (setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, (char *)&on, + sizeof (int)) == -1) + { + sock_geterrmsg(errbuf, errbuflen, + "setsockopt(SO_NOSIGPIPE) failed"); + closesocket(sock); + return INVALID_SOCKET; + } +#endif + return sock; +} + /* * \brief It initializes a network connection both from the client and the server side. * @@ -271,7 +476,10 @@ static int sock_ismcastaddr(const struct sockaddr *saddr) * * In case of a server socket, the function calls socket(), bind() and listen(). * - * This function is usually preceeded by the sock_initaddress(). + * This function is usually preceded by the sock_initaddress(). + * + * \param host: for client sockets, the host name to which we're trying + * to connect. * * \param addrinfo: pointer to an addrinfo variable which will be used to * open the socket and such. This variable is the one returned by the previous call to @@ -293,48 +501,33 @@ static int sock_ismcastaddr(const struct sockaddr *saddr) * if everything is fine, INVALID_SOCKET if some errors occurred. The error message is returned * in the 'errbuf' variable. */ -SOCKET sock_open(struct addrinfo *addrinfo, int server, int nconn, char *errbuf, int errbuflen) +SOCKET sock_open(const char *host, struct addrinfo *addrinfo, int server, int nconn, char *errbuf, int errbuflen) { SOCKET sock; -#if defined(SO_NOSIGPIPE) || defined(IPV6_V6ONLY) || defined(IPV6_BINDV6ONLY) - int on = 1; -#endif - - sock = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol); - if (sock == INVALID_SOCKET) - { - sock_geterror("socket()", errbuf, errbuflen); - return INVALID_SOCKET; - } - - /* - * Disable SIGPIPE, if we have SO_NOSIGPIPE. We don't want to - * have to deal with signals if the peer closes the connection, - * especially in client programs, which may not even be aware that - * they're sending to sockets. - */ -#ifdef SO_NOSIGPIPE - if (setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, (char *)&on, - sizeof (int)) == -1) - { - sock_geterror("setsockopt(SO_NOSIGPIPE)", errbuf, errbuflen); - closesocket(sock); - return INVALID_SOCKET; - } -#endif /* This is a server socket */ if (server) { + int on; + + /* + * Attempt to create the socket. + */ + sock = sock_create_socket(addrinfo, errbuf, errbuflen); + if (sock == INVALID_SOCKET) + { + return INVALID_SOCKET; + } + /* * Allow a new server to bind the socket after the old one * exited, even if lingering sockets are still present. * * Don't treat an error as a failure. */ - int optval = 1; + on = 1; (void)setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, - (char *)&optval, sizeof (optval)); + (char *)&on, sizeof (on)); #if defined(IPV6_V6ONLY) || defined(IPV6_BINDV6ONLY) /* @@ -371,11 +564,12 @@ SOCKET sock_open(struct addrinfo *addrinfo, int server, int nconn, char *errbuf, #endif /* IPV6_V6ONLY */ if (addrinfo->ai_family == PF_INET6) { + on = 1; if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on, sizeof (int)) == -1) { if (errbuf) - pcap_snprintf(errbuf, errbuflen, "setsockopt(IPV6_V6ONLY)"); + snprintf(errbuf, errbuflen, "setsockopt(IPV6_V6ONLY)"); closesocket(sock); return INVALID_SOCKET; } @@ -385,7 +579,7 @@ SOCKET sock_open(struct addrinfo *addrinfo, int server, int nconn, char *errbuf, /* WARNING: if the address is a mcast one, I should place the proper Win32 code here */ if (bind(sock, addrinfo->ai_addr, (int) addrinfo->ai_addrlen) != 0) { - sock_geterror("bind()", errbuf, errbuflen); + sock_geterrmsg(errbuf, errbuflen, "bind() failed"); closesocket(sock); return INVALID_SOCKET; } @@ -393,7 +587,8 @@ SOCKET sock_open(struct addrinfo *addrinfo, int server, int nconn, char *errbuf, if (addrinfo->ai_socktype == SOCK_STREAM) if (listen(sock, nconn) == -1) { - sock_geterror("listen()", errbuf, errbuflen); + sock_geterrmsg(errbuf, errbuflen, + "listen() failed"); closesocket(sock); return INVALID_SOCKET; } @@ -403,68 +598,259 @@ SOCKET sock_open(struct addrinfo *addrinfo, int server, int nconn, char *errbuf, } else /* we're the client */ { + struct addr_status *addrs_to_try; struct addrinfo *tempaddrinfo; - char *errbufptr; - size_t bufspaceleft; - - tempaddrinfo = addrinfo; - errbufptr = errbuf; - bufspaceleft = errbuflen; - *errbufptr = 0; + size_t numaddrinfos; + size_t i; + int current_af = AF_UNSPEC; /* - * We have to loop though all the addinfo returned. - * For instance, we can have both IPv6 and IPv4 addresses, but the service we're trying - * to connect to is unavailable in IPv6, so we have to try in IPv4 as well + * We have to loop though all the addrinfos returned. + * For instance, we can have both IPv6 and IPv4 addresses, + * but the service we're trying to connect to is unavailable + * in IPv6, so we have to try in IPv4 as well. + * + * How many addrinfos do we have? */ - while (tempaddrinfo) + numaddrinfos = 0; + for (tempaddrinfo = addrinfo; tempaddrinfo != NULL; + tempaddrinfo = tempaddrinfo->ai_next) { + numaddrinfos++; + } + if (numaddrinfos == 0) + { + snprintf(errbuf, errbuflen, + "There are no addresses in the address list"); + return INVALID_SOCKET; + } + + /* + * Allocate an array of struct addr_status and fill it in. + */ + addrs_to_try = calloc(numaddrinfos, sizeof *addrs_to_try); + if (addrs_to_try == NULL) + { + snprintf(errbuf, errbuflen, + "Out of memory connecting to %s", host); + return INVALID_SOCKET; + } + + for (tempaddrinfo = addrinfo, i = 0; tempaddrinfo != NULL; + tempaddrinfo = tempaddrinfo->ai_next, i++) + { + addrs_to_try[i].info = tempaddrinfo; + addrs_to_try[i].errcode = 0; + addrs_to_try[i].errtype = SOCK_NOERR; + } + + /* + * Sort the structures to put the IPv4 addresses before the + * IPv6 addresses; we will have to create an IPv4 socket + * for the IPv4 addresses and an IPv6 socket for the IPv6 + * addresses (one of the arguments to socket() is the + * address/protocol family to use, and IPv4 and IPv6 are + * separate address/protocol families). + */ + qsort(addrs_to_try, numaddrinfos, sizeof *addrs_to_try, + compare_addrs_to_try_by_address_family); + + /* Start out with no socket. */ + sock = INVALID_SOCKET; + + /* + * Now try them all. + */ + for (i = 0; i < numaddrinfos; i++) + { + tempaddrinfo = addrs_to_try[i].info; +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + break; +#endif + /* + * If we have a socket, but it's for a + * different address family, close it. + */ + if (sock != INVALID_SOCKET && + current_af != tempaddrinfo->ai_family) + { + closesocket(sock); + sock = INVALID_SOCKET; + } + + /* + * If we don't have a socket, open one + * for *this* address's address family. + */ + if (sock == INVALID_SOCKET) + { + sock = sock_create_socket(tempaddrinfo, + errbuf, errbuflen); + if (sock == INVALID_SOCKET) + { + free(addrs_to_try); + return INVALID_SOCKET; + } + } if (connect(sock, tempaddrinfo->ai_addr, (int) tempaddrinfo->ai_addrlen) == -1) { - size_t msglen; - char TmpBuffer[100]; - char SocketErrorMessage[SOCK_ERRBUF_SIZE]; - - /* - * We have to retrieve the error message before any other socket call completes, otherwise - * the error message is lost - */ - sock_geterror("Connect to socket failed", - SocketErrorMessage, sizeof(SocketErrorMessage)); - - /* Returns the numeric address of the host that triggered the error */ - sock_getascii_addrport((struct sockaddr_storage *) tempaddrinfo->ai_addr, TmpBuffer, sizeof(TmpBuffer), NULL, 0, NI_NUMERICHOST, TmpBuffer, sizeof(TmpBuffer)); - - pcap_snprintf(errbufptr, bufspaceleft, - "Is the server properly installed on %s? %s", TmpBuffer, SocketErrorMessage); - - /* In case more then one 'connect' fails, we manage to keep all the error messages */ - msglen = strlen(errbufptr); - - errbufptr[msglen] = ' '; - errbufptr[msglen + 1] = 0; - - bufspaceleft = bufspaceleft - (msglen + 1); - errbufptr += (msglen + 1); - - tempaddrinfo = tempaddrinfo->ai_next; + addrs_to_try[i].errcode = sock_geterrcode(); + addrs_to_try[i].errtype = + sock_geterrtype(addrs_to_try[i].errcode); } else break; } /* - * Check how we exit from the previous loop - * If tempaddrinfo is equal to NULL, it means that all the connect() failed. + * Check how we exited from the previous loop. + * If tempaddrinfo is equal to NULL, it means that all + * the connect() attempts failed. Construct an + * error message. */ - if (tempaddrinfo == NULL) + if (i == numaddrinfos) { + int same_error_for_all; + int first_error; + closesocket(sock); + + /* + * Sort the statuses to group together categories + * of errors, errors within categories, and + * address families within error sets. + */ + qsort(addrs_to_try, numaddrinfos, sizeof *addrs_to_try, + compare_addrs_to_try_by_status); + + /* + * Are all the errors the same? + */ + same_error_for_all = 1; + first_error = addrs_to_try[0].errcode; + for (i = 1; i < numaddrinfos; i++) + { + if (addrs_to_try[i].errcode != first_error) + { + same_error_for_all = 0; + break; + } + } + + if (same_error_for_all) { + /* + * Yes. No need to show the IP + * addresses. + */ + if (addrs_to_try[0].errtype == SOCK_CONNERR) { + /* + * Connection error; note that + * the daemon might not be set + * up correctly, or set up at all. + */ + sock_fmterrmsg(errbuf, errbuflen, + addrs_to_try[0].errcode, + "Is the server properly installed? Cannot connect to %s", + host); + } else { + sock_fmterrmsg(errbuf, errbuflen, + addrs_to_try[0].errcode, + "Cannot connect to %s", host); + } + } else { + /* + * Show all the errors and the IP addresses + * to which they apply. + */ + char *errbufptr; + size_t bufspaceleft; + size_t msglen; + + snprintf(errbuf, errbuflen, + "Connect to %s failed: ", host); + + msglen = strlen(errbuf); + errbufptr = errbuf + msglen; + bufspaceleft = errbuflen - msglen; + + for (i = 0; i < numaddrinfos && + addrs_to_try[i].errcode != SOCK_NOERR; + i++) + { + /* + * Get the string for the address + * and port that got this error. + */ + sock_getascii_addrport((struct sockaddr_storage *) addrs_to_try[i].info->ai_addr, + errbufptr, (int)bufspaceleft, + NULL, 0, NI_NUMERICHOST, NULL, 0); + msglen = strlen(errbuf); + errbufptr = errbuf + msglen; + bufspaceleft = errbuflen - msglen; + + if (i + 1 < numaddrinfos && + addrs_to_try[i + 1].errcode == addrs_to_try[i].errcode) + { + /* + * There's another error + * after this, and it has + * the same error code. + * + * Append a comma, as the + * list of addresses with + * this error has another + * entry. + */ + snprintf(errbufptr, bufspaceleft, + ", "); + } + else + { + /* + * Either there are no + * more errors after this, + * or the next error is + * different. + * + * Append a colon and + * the message for tis + * error, followed by a + * comma if there are + * more errors. + */ + sock_fmterrmsg(errbufptr, + bufspaceleft, + addrs_to_try[i].errcode, + "%s", ""); + msglen = strlen(errbuf); + errbufptr = errbuf + msglen; + bufspaceleft = errbuflen - msglen; + + if (i + 1 < numaddrinfos && + addrs_to_try[i + 1].errcode != SOCK_NOERR) + { + /* + * More to come. + */ + snprintf(errbufptr, + bufspaceleft, + ", "); + } + } + msglen = strlen(errbuf); + errbufptr = errbuf + msglen; + bufspaceleft = errbuflen - msglen; + } + } + free(addrs_to_try); return INVALID_SOCKET; } else + { + free(addrs_to_try); return sock; + } } } @@ -495,7 +881,7 @@ int sock_close(SOCKET sock, char *errbuf, int errbuflen) */ if (shutdown(sock, SHUT_WR)) { - sock_geterror("shutdown()", errbuf, errbuflen); + sock_geterrmsg(errbuf, errbuflen, "shutdown() feiled"); /* close the socket anyway */ closesocket(sock); return -1; @@ -506,7 +892,7 @@ int sock_close(SOCKET sock, char *errbuf, int errbuflen) } /* - * gai_errstring() has some problems: + * gai_strerror() has some problems: * * 1) on Windows, Microsoft explicitly says it's not thread-safe; * 2) on UN*X, the Single UNIX Specification doesn't say it *is* @@ -527,52 +913,52 @@ get_gai_errstring(char *errbuf, int errbuflen, const char *prefix, int err, char hostport[PCAP_ERRBUF_SIZE]; if (hostname != NULL && portname != NULL) - pcap_snprintf(hostport, PCAP_ERRBUF_SIZE, "%s:%s", + snprintf(hostport, PCAP_ERRBUF_SIZE, "host and port %s:%s", hostname, portname); else if (hostname != NULL) - pcap_snprintf(hostport, PCAP_ERRBUF_SIZE, "%s", + snprintf(hostport, PCAP_ERRBUF_SIZE, "host %s", hostname); else if (portname != NULL) - pcap_snprintf(hostport, PCAP_ERRBUF_SIZE, ":%s", + snprintf(hostport, PCAP_ERRBUF_SIZE, "port %s", portname); else - pcap_snprintf(hostport, PCAP_ERRBUF_SIZE, ""); + snprintf(hostport, PCAP_ERRBUF_SIZE, ""); switch (err) { #ifdef EAI_ADDRFAMILY case EAI_ADDRFAMILY: - pcap_snprintf(errbuf, errbuflen, + snprintf(errbuf, errbuflen, "%sAddress family for %s not supported", prefix, hostport); break; #endif case EAI_AGAIN: - pcap_snprintf(errbuf, errbuflen, + snprintf(errbuf, errbuflen, "%s%s could not be resolved at this time", prefix, hostport); break; case EAI_BADFLAGS: - pcap_snprintf(errbuf, errbuflen, + snprintf(errbuf, errbuflen, "%sThe ai_flags parameter for looking up %s had an invalid value", prefix, hostport); break; case EAI_FAIL: - pcap_snprintf(errbuf, errbuflen, + snprintf(errbuf, errbuflen, "%sA non-recoverable error occurred when attempting to resolve %s", prefix, hostport); break; case EAI_FAMILY: - pcap_snprintf(errbuf, errbuflen, + snprintf(errbuf, errbuflen, "%sThe address family for looking up %s was not recognized", prefix, hostport); break; case EAI_MEMORY: - pcap_snprintf(errbuf, errbuflen, + snprintf(errbuf, errbuflen, "%sOut of memory trying to allocate storage when looking up %s", prefix, hostport); break; @@ -589,26 +975,26 @@ get_gai_errstring(char *errbuf, int errbuflen, const char *prefix, int err, */ #if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME case EAI_NODATA: - pcap_snprintf(errbuf, errbuflen, + snprintf(errbuf, errbuflen, "%sNo address associated with %s", prefix, hostport); break; #endif case EAI_NONAME: - pcap_snprintf(errbuf, errbuflen, - "%sThe host name %s couldn't be resolved", + snprintf(errbuf, errbuflen, + "%sThe %s couldn't be resolved", prefix, hostport); break; case EAI_SERVICE: - pcap_snprintf(errbuf, errbuflen, + snprintf(errbuf, errbuflen, "%sThe service value specified when looking up %s as not recognized for the socket type", prefix, hostport); break; case EAI_SOCKTYPE: - pcap_snprintf(errbuf, errbuflen, + snprintf(errbuf, errbuflen, "%sThe socket type specified when looking up %s as not recognized", prefix, hostport); break; @@ -618,15 +1004,15 @@ get_gai_errstring(char *errbuf, int errbuflen, const char *prefix, int err, /* * Assumed to be UN*X. */ - pcap_snprintf(errbuf, errbuflen, - "%sAn error occurred when looking up %s: %s", - prefix, hostport, pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(errbuf, errbuflen, errno, + "%sAn error occurred when looking up %s", + prefix, hostport); break; #endif #ifdef EAI_BADHINTS case EAI_BADHINTS: - pcap_snprintf(errbuf, errbuflen, + snprintf(errbuf, errbuflen, "%sInvalid value for hints when looking up %s", prefix, hostport); break; @@ -634,7 +1020,7 @@ get_gai_errstring(char *errbuf, int errbuflen, const char *prefix, int err, #ifdef EAI_PROTOCOL case EAI_PROTOCOL: - pcap_snprintf(errbuf, errbuflen, + snprintf(errbuf, errbuflen, "%sResolved protocol when looking up %s is unknown", prefix, hostport); break; @@ -642,14 +1028,14 @@ get_gai_errstring(char *errbuf, int errbuflen, const char *prefix, int err, #ifdef EAI_OVERFLOW case EAI_OVERFLOW: - pcap_snprintf(errbuf, errbuflen, + snprintf(errbuf, errbuflen, "%sArgument buffer overflow when looking up %s", prefix, hostport); break; #endif default: - pcap_snprintf(errbuf, errbuflen, + snprintf(errbuf, errbuflen, "%sgetaddrinfo() error %d when looking up %s", prefix, err, hostport); break; @@ -699,13 +1085,58 @@ int sock_initaddress(const char *host, const char *port, { int retval; - retval = getaddrinfo(host, port, hints, addrinfo); + /* + * We allow both the host and port to be null, but getaddrinfo() + * is not guaranteed to do so; to handle that, if port is null, + * we provide "0" as the port number. + * + * This results in better error messages from get_gai_errstring(), + * as those messages won't talk about a problem with the port if + * no port was specified. + */ + retval = getaddrinfo(host, port == NULL ? "0" : port, hints, addrinfo); if (retval != 0) { if (errbuf) { - get_gai_errstring(errbuf, errbuflen, "", retval, - host, port); + if (host != NULL && port != NULL) { + /* + * Try with just a host, to distinguish + * between "host is bad" and "port is + * bad". + */ + int try_retval; + + try_retval = getaddrinfo(host, NULL, hints, + addrinfo); + if (try_retval == 0) { + /* + * Worked with just the host, + * so assume the problem is + * with the port. + * + * Free up the address info first. + */ + freeaddrinfo(*addrinfo); + get_gai_errstring(errbuf, errbuflen, + "", retval, NULL, port); + } else { + /* + * Didn't work with just the host, + * so assume the problem is + * with the host. + */ + get_gai_errstring(errbuf, errbuflen, + "", retval, host, NULL); + } + } else { + /* + * Either the host or port was null, so + * there's nothing to determine. + */ + get_gai_errstring(errbuf, errbuflen, "", + retval, host, port); + } } return -1; } @@ -726,7 +1157,7 @@ int sock_initaddress(const char *host, const char *port, ((*addrinfo)->ai_family != PF_INET6)) { if (errbuf) - pcap_snprintf(errbuf, errbuflen, "getaddrinfo(): socket type not supported"); + snprintf(errbuf, errbuflen, "getaddrinfo(): socket type not supported"); freeaddrinfo(*addrinfo); *addrinfo = NULL; return -1; @@ -739,7 +1170,7 @@ int sock_initaddress(const char *host, const char *port, (sock_ismcastaddr((*addrinfo)->ai_addr) == 0)) { if (errbuf) - pcap_snprintf(errbuf, errbuflen, "getaddrinfo(): multicast addresses are not valid when using TCP streams"); + snprintf(errbuf, errbuflen, "getaddrinfo(): multicast addresses are not valid when using TCP streams"); freeaddrinfo(*addrinfo); *addrinfo = NULL; return -1; @@ -775,7 +1206,7 @@ int sock_initaddress(const char *host, const char *port, * '-2' if we got one of those errors. * For errors, an error message is returned in the 'errbuf' variable. */ -int sock_send(SOCKET sock, const char *buffer, size_t size, +int sock_send(SOCKET sock, SSL *ssl _U_NOSSL_, const char *buffer, size_t size, char *errbuf, int errbuflen) { int remaining; @@ -785,7 +1216,7 @@ int sock_send(SOCKET sock, const char *buffer, size_t size, { if (errbuf) { - pcap_snprintf(errbuf, errbuflen, + snprintf(errbuf, errbuflen, "Can't send more than %u bytes with sock_send", INT_MAX); } @@ -794,6 +1225,13 @@ int sock_send(SOCKET sock, const char *buffer, size_t size, remaining = (int)size; do { +#ifdef HAVE_OPENSSL + if (ssl) return ssl_send(ssl, buffer, remaining, errbuf, errbuflen); +#endif + +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + nsent = remaining; +#else #ifdef MSG_NOSIGNAL /* * Send with MSG_NOSIGNAL, so that we don't get SIGPIPE @@ -805,6 +1243,7 @@ int sock_send(SOCKET sock, const char *buffer, size_t size, #else nsent = send(sock, buffer, remaining, 0); #endif +#endif //FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION if (nsent == -1) { @@ -828,7 +1267,8 @@ int sock_send(SOCKET sock, const char *buffer, size_t size, */ return -2; } - sock_fmterror("send()", errcode, errbuf, errbuflen); + sock_fmterrmsg(errbuf, errbuflen, errcode, + "send() failed"); #else errcode = errno; if (errcode == ECONNRESET || errcode == EPIPE) @@ -840,7 +1280,8 @@ int sock_send(SOCKET sock, const char *buffer, size_t size, */ return -2; } - sock_fmterror("send()", errcode, errbuf, errbuflen); + sock_fmterrmsg(errbuf, errbuflen, errcode, + "send() failed"); #endif return -1; } @@ -853,11 +1294,11 @@ int sock_send(SOCKET sock, const char *buffer, size_t size, } /* - * \brief It copies the amount of data contained into 'buffer' into 'tempbuf'. + * \brief It copies the amount of data contained in 'data' into 'outbuf'. * and it checks for buffer overflows. * - * This function basically copies 'size' bytes of data contained into 'buffer' - * into 'tempbuf', starting at offset 'offset'. Before that, it checks that the + * This function basically copies 'size' bytes of data contained in 'data' + * into 'outbuf', starting at offset 'offset'. Before that, it checks that the * resulting buffer will not be larger than 'totsize'. Finally, it updates * the 'offset' variable in order to point to the first empty location of the buffer. * @@ -866,25 +1307,24 @@ int sock_send(SOCKET sock, const char *buffer, size_t size, * 'offset' variable. This mode can be useful when the buffer already contains the * data (maybe because the producer writes directly into the target buffer), so * only the buffer overflow check has to be made. - * In this case, both 'buffer' and 'tempbuf' can be NULL values. + * In this case, both 'data' and 'outbuf' can be NULL values. * * This function is useful in case the userland application does not know immediately * all the data it has to write into the socket. This function provides a way to create * the "stream" step by step, appending the new data to the old one. Then, when all the * data has been bufferized, the application can call the sock_send() function. * - * \param buffer: a char pointer to a user-allocated buffer that keeps the data - * that has to be copied. + * \param data: a void pointer to the data that has to be copied. * * \param size: number of bytes that have to be copied. * - * \param tempbuf: user-allocated buffer (of size 'totsize') in which data + * \param outbuf: user-allocated buffer (of size 'totsize') into which data * has to be copied. * - * \param offset: an index into 'tempbuf' which keeps the location of its first + * \param offset: an index into 'outbuf' which keeps the location of its first * empty location. * - * \param totsize: total size of the buffer in which data is being copied. + * \param totsize: total size of the buffer into which data is being copied. * * \param checkonly: '1' if we do not want to copy data into the buffer and we * want just do a buffer ovreflow control, '0' if data has to be copied as well. @@ -897,7 +1337,7 @@ int sock_send(SOCKET sock, const char *buffer, size_t size, * larger than 'errbuflen - 1' because the last char is reserved for the string terminator. * * \return '0' if everything is fine, '-1' if some errors occurred. The error message - * is returned in the 'errbuf' variable. When the function returns, 'tempbuf' will + * is returned in the 'errbuf' variable. When the function returns, 'outbuf' will * have the new string appended, and 'offset' will keep the length of that buffer. * In case of 'checkonly == 1', data is not copied, but 'offset' is updated in any case. * @@ -907,17 +1347,17 @@ int sock_send(SOCKET sock, const char *buffer, size_t size, * \warning In case of 'checkonly', be carefully to call this function *before* copying * the data into the buffer. Otherwise, the control about the buffer overflow is useless. */ -int sock_bufferize(const char *buffer, int size, char *tempbuf, int *offset, int totsize, int checkonly, char *errbuf, int errbuflen) +int sock_bufferize(const void *data, int size, char *outbuf, int *offset, int totsize, int checkonly, char *errbuf, int errbuflen) { if ((*offset + size) > totsize) { if (errbuf) - pcap_snprintf(errbuf, errbuflen, "Not enough space in the temporary send buffer."); + snprintf(errbuf, errbuflen, "Not enough space in the temporary send buffer."); return -1; } if (!checkonly) - memcpy(tempbuf + (*offset), buffer, size); + memcpy(outbuf + (*offset), data, size); (*offset) += size; @@ -970,9 +1410,10 @@ int sock_bufferize(const char *buffer, int size, char *tempbuf, int *offset, int * The error message is returned in the 'errbuf' variable. */ -int sock_recv(SOCKET sock, void *buffer, size_t size, int flags, - char *errbuf, int errbuflen) +int sock_recv(SOCKET sock, SSL *ssl _U_NOSSL_, void *buffer, size_t size, + int flags, char *errbuf, int errbuflen) { + int recv_flags = 0; char *bufp = buffer; int remaining; ssize_t nread; @@ -985,13 +1426,16 @@ int sock_recv(SOCKET sock, void *buffer, size_t size, int flags, { if (errbuf) { - pcap_snprintf(errbuf, errbuflen, + snprintf(errbuf, errbuflen, "Can't read more than %u bytes with sock_recv", INT_MAX); } return -1; } + if (flags & SOCK_MSG_PEEK) + recv_flags |= MSG_PEEK; + bufp = (char *) buffer; remaining = (int) size; @@ -1000,7 +1444,22 @@ int sock_recv(SOCKET sock, void *buffer, size_t size, int flags, * Win32. */ for (;;) { - nread = recv(sock, bufp, remaining, 0); +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + nread = fuzz_recv(bufp, remaining); +#elif defined(HAVE_OPENSSL) + if (ssl) + { + /* + * XXX - what about MSG_PEEK? + */ + nread = ssl_recv(ssl, bufp, remaining, errbuf, errbuflen); + if (nread == -2) return -1; + } + else + nread = recv(sock, bufp, remaining, recv_flags); +#else + nread = recv(sock, bufp, remaining, recv_flags); +#endif if (nread == -1) { @@ -1008,7 +1467,7 @@ int sock_recv(SOCKET sock, void *buffer, size_t size, int flags, if (errno == EINTR) return -3; #endif - sock_geterror("recv()", errbuf, errbuflen); + sock_geterrmsg(errbuf, errbuflen, "recv() failed"); return -1; } @@ -1024,7 +1483,7 @@ int sock_recv(SOCKET sock, void *buffer, size_t size, int flags, */ if (errbuf) { - pcap_snprintf(errbuf, errbuflen, + snprintf(errbuf, errbuflen, "The other host terminated the connection."); } return -1; @@ -1058,7 +1517,7 @@ int sock_recv(SOCKET sock, void *buffer, size_t size, int flags, * * Returns the size of the datagram on success or -1 on error. */ -int sock_recv_dgram(SOCKET sock, void *buffer, size_t size, +int sock_recv_dgram(SOCKET sock, SSL *ssl _U_NOSSL_, void *buffer, size_t size, char *errbuf, int errbuflen) { ssize_t nread; @@ -1075,20 +1534,29 @@ int sock_recv_dgram(SOCKET sock, void *buffer, size_t size, { if (errbuf) { - pcap_snprintf(errbuf, errbuflen, + snprintf(errbuf, errbuflen, "Can't read more than %u bytes with sock_recv_dgram", INT_MAX); } return -1; } +#ifdef HAVE_OPENSSL + // TODO: DTLS + if (ssl) + { + snprintf(errbuf, errbuflen, "DTLS not implemented yet"); + return -1; + } +#endif + /* * This should be a datagram socket, so we should get the * entire datagram in one recv() or recvmsg() call, and * don't need to loop. */ #ifdef _WIN32 - nread = recv(sock, buffer, size, 0); + nread = recv(sock, buffer, (int)size, 0); if (nread == SOCKET_ERROR) { /* @@ -1104,7 +1572,8 @@ int sock_recv_dgram(SOCKET sock, void *buffer, size_t size, * supplied to us, the excess data is discarded, * and we'll report an error. */ - sock_geterror("recv()", errbuf, errbuflen); + sock_fmterrmsg(errbuf, errbuflen, sock_geterrcode(), + "recv() failed"); return -1; } #else /* _WIN32 */ @@ -1132,12 +1601,16 @@ int sock_recv_dgram(SOCKET sock, void *buffer, size_t size, #ifdef HAVE_STRUCT_MSGHDR_MSG_FLAGS message.msg_flags = 0; #endif +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + nread = fuzz_recv(buffer, size); +#else nread = recvmsg(sock, &message, 0); +#endif if (nread == -1) { if (errno == EINTR) return -3; - sock_geterror("recv()", errbuf, errbuflen); + sock_geterrmsg(errbuf, errbuflen, "recv() failed"); return -1; } #ifdef HAVE_STRUCT_MSGHDR_MSG_FLAGS @@ -1154,7 +1627,7 @@ int sock_recv_dgram(SOCKET sock, void *buffer, size_t size, * Report this as an error, as the Microsoft documentation * implies we'd do in a similar case on Windows. */ - pcap_snprintf(errbuf, errbuflen, "recv(): Message too long"); + snprintf(errbuf, errbuflen, "recv(): Message too long"); return -1; } #endif /* HAVE_STRUCT_MSGHDR_MSG_FLAGS */ @@ -1192,7 +1665,7 @@ int sock_recv_dgram(SOCKET sock, void *buffer, size_t size, * \return '0' if everything is fine, '-1' if some errors occurred. * The error message is returned in the 'errbuf' variable. */ -int sock_discard(SOCKET sock, int size, char *errbuf, int errbuflen) +int sock_discard(SOCKET sock, SSL *ssl, int size, char *errbuf, int errbuflen) { #define TEMP_BUF_SIZE 32768 @@ -1208,7 +1681,7 @@ int sock_discard(SOCKET sock, int size, char *errbuf, int errbuflen) */ while (size > TEMP_BUF_SIZE) { - if (sock_recv(sock, buffer, TEMP_BUF_SIZE, SOCK_RECEIVEALL_YES, errbuf, errbuflen) == -1) + if (sock_recv(sock, ssl, buffer, TEMP_BUF_SIZE, SOCK_RECEIVEALL_YES, errbuf, errbuflen) == -1) return -1; size -= TEMP_BUF_SIZE; @@ -1220,7 +1693,7 @@ int sock_discard(SOCKET sock, int size, char *errbuf, int errbuflen) */ if (size) { - if (sock_recv(sock, buffer, size, SOCK_RECEIVEALL_YES, errbuf, errbuflen) == -1) + if (sock_recv(sock, ssl, buffer, size, SOCK_RECEIVEALL_YES, errbuf, errbuflen) == -1) return -1; } @@ -1273,7 +1746,8 @@ int sock_check_hostlist(char *hostlist, const char *sep, struct sockaddr_storage temphostlist = strdup(hostlist); if (temphostlist == NULL) { - sock_geterror("sock_check_hostlist(), malloc() failed", errbuf, errbuflen); + sock_geterrmsg(errbuf, errbuflen, + "sock_check_hostlist(), malloc() failed"); return -2; } @@ -1358,7 +1832,7 @@ int sock_check_hostlist(char *hostlist, const char *sep, struct sockaddr_storage * the host wasn't in the list. */ if (errbuf) - pcap_snprintf(errbuf, errbuflen, "The host is not in the allowed host list. Connection refused."); + snprintf(errbuf, errbuflen, "The host is not in the allowed host list. Connection refused."); return -1; } } @@ -1459,7 +1933,7 @@ int sock_getmyinfo(SOCKET sock, char *address, int addrlen, char *port, int port if (getsockname(sock, (struct sockaddr *) &mysockaddr, &sockaddrlen) == -1) { - sock_geterror("getsockname()", errbuf, errbuflen); + sock_geterrmsg(errbuf, errbuflen, "getsockname() failed"); return 0; } @@ -1515,7 +1989,7 @@ int sock_getmyinfo(SOCKET sock, char *address, int addrlen, char *port, int port * and 'port'. * In any case, the returned strings are '0' terminated. */ -int sock_getascii_addrport(const struct sockaddr_storage *sockaddr, char *address, int addrlen, char *port, int portlen, int flags, char *errbuf, int errbuflen) +int sock_getascii_addrport(const struct sockaddr_storage *sockaddr, char *address, int addrlen, char *port, int portlen, int flags, char *errbuf, size_t errbuflen) { socklen_t sockaddrlen; int retval; /* Variable that keeps the return value; */ @@ -1547,7 +2021,8 @@ int sock_getascii_addrport(const struct sockaddr_storage *sockaddr, char *addres /* If the user wants to receive an error message */ if (errbuf) { - sock_geterror("getnameinfo()", errbuf, errbuflen); + sock_geterrmsg(errbuf, errbuflen, + "getnameinfo() failed"); errbuf[errbuflen - 1] = 0; } @@ -1628,7 +2103,7 @@ int sock_present2network(const char *address, struct sockaddr_storage *sockaddr, freeaddrinfo(addrinfo); if (errbuf) - pcap_snprintf(errbuf, errbuflen, "More than one socket requested; using the first one returned"); + snprintf(errbuf, errbuflen, "More than one socket requested; using the first one returned"); return -2; } diff --git a/sockutils.h b/sockutils.h index 8a45b3df423f..a488d8fcb4ff 100644 --- a/sockutils.h +++ b/sockutils.h @@ -37,6 +37,10 @@ #pragma once #endif +#include /* we declare varargs functions */ + +#include "pcap/funcattrs.h" + #include "pcap/socket.h" #ifndef _WIN32 @@ -52,6 +56,8 @@ #define closesocket(a) close(a) #endif +#include "sslutils.h" // for SSL type, whatever that turns out to be + /* * MingW headers include this definition, but only for Windows XP and above. * MSDN states that this function is available for most versions on Windows. @@ -104,6 +110,8 @@ int WSAAPI getnameinfo(const struct sockaddr*,socklen_t,char*,DWORD, #define SOCK_EOF_ISNT_ERROR 0x00000000 /* Return 0 on EOF */ #define SOCK_EOF_IS_ERROR 0x00000002 /* Return an error on EOF */ +#define SOCK_MSG_PEEK 0x00000004 /* Return data but leave it in the socket queue */ + /* * \} */ @@ -123,28 +131,33 @@ extern "C" { int sock_init(char *errbuf, int errbuflen); void sock_cleanup(void); -void sock_fmterror(const char *caller, int errcode, char *errbuf, int errbuflen); -void sock_geterror(const char *caller, char *errbuf, int errbufsize); +int sock_geterrcode(void); +void sock_vfmterrmsg(char *errbuf, size_t errbuflen, int errcode, + PCAP_FORMAT_STRING(const char *fmt), va_list ap) PCAP_PRINTFLIKE(4, 0); +void sock_fmterrmsg(char *errbuf, size_t errbuflen, int errcode, + PCAP_FORMAT_STRING(const char *fmt), ...) PCAP_PRINTFLIKE(4, 5); +void sock_geterrmsg(char *errbuf, size_t errbuflen, + PCAP_FORMAT_STRING(const char *fmt), ...) PCAP_PRINTFLIKE(3, 4); int sock_initaddress(const char *address, const char *port, struct addrinfo *hints, struct addrinfo **addrinfo, char *errbuf, int errbuflen); -int sock_recv(SOCKET sock, void *buffer, size_t size, int receiveall, +int sock_recv(SOCKET sock, SSL *, void *buffer, size_t size, int receiveall, char *errbuf, int errbuflen); -int sock_recv_dgram(SOCKET sock, void *buffer, size_t size, +int sock_recv_dgram(SOCKET sock, SSL *, void *buffer, size_t size, char *errbuf, int errbuflen); -SOCKET sock_open(struct addrinfo *addrinfo, int server, int nconn, char *errbuf, int errbuflen); +SOCKET sock_open(const char *host, struct addrinfo *addrinfo, int server, int nconn, char *errbuf, int errbuflen); int sock_close(SOCKET sock, char *errbuf, int errbuflen); -int sock_send(SOCKET sock, const char *buffer, size_t size, +int sock_send(SOCKET sock, SSL *, const char *buffer, size_t size, char *errbuf, int errbuflen); -int sock_bufferize(const char *buffer, int size, char *tempbuf, int *offset, int totsize, int checkonly, char *errbuf, int errbuflen); -int sock_discard(SOCKET sock, int size, char *errbuf, int errbuflen); +int sock_bufferize(const void *data, int size, char *outbuf, int *offset, int totsize, int checkonly, char *errbuf, int errbuflen); +int sock_discard(SOCKET sock, SSL *, int size, char *errbuf, int errbuflen); int sock_check_hostlist(char *hostlist, const char *sep, struct sockaddr_storage *from, char *errbuf, int errbuflen); int sock_cmpaddr(struct sockaddr_storage *first, struct sockaddr_storage *second); int sock_getmyinfo(SOCKET sock, char *address, int addrlen, char *port, int portlen, int flags, char *errbuf, int errbuflen); -int sock_getascii_addrport(const struct sockaddr_storage *sockaddr, char *address, int addrlen, char *port, int portlen, int flags, char *errbuf, int errbuflen); +int sock_getascii_addrport(const struct sockaddr_storage *sockaddr, char *address, int addrlen, char *port, int portlen, int flags, char *errbuf, size_t errbuflen); int sock_present2network(const char *address, struct sockaddr_storage *sockaddr, int addr_family, char *errbuf, int errbuflen); #ifdef __cplusplus diff --git a/sslutils.c b/sslutils.c new file mode 100644 index 000000000000..7274cc34c580 --- /dev/null +++ b/sslutils.c @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2002 - 2003 + * NetGroup, Politecnico di Torino (Italy) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Politecnico di Torino nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_OPENSSL +#include + +#include "portability.h" + +#include "sslutils.h" + +static const char *ssl_keyfile = ""; //!< file containing the private key in PEM format +static const char *ssl_certfile = ""; //!< file containing the server's certificate in PEM format +static const char *ssl_rootfile = ""; //!< file containing the list of CAs trusted by the client +// TODO: a way to set ssl_rootfile from the command line, or an envvar? + +// TODO: lock? +static SSL_CTX *ctx; + +void ssl_set_certfile(const char *certfile) +{ + ssl_certfile = certfile; +} + +void ssl_set_keyfile(const char *keyfile) +{ + ssl_keyfile = keyfile; +} + +int ssl_init_once(int is_server, int enable_compression, char *errbuf, size_t errbuflen) +{ + static int inited = 0; + if (inited) return 0; + + SSL_library_init(); + SSL_load_error_strings(); + OpenSSL_add_ssl_algorithms(); + if (enable_compression) + SSL_COMP_get_compression_methods(); + + SSL_METHOD const *meth = + is_server ? SSLv23_server_method() : SSLv23_client_method(); + ctx = SSL_CTX_new(meth); + if (! ctx) + { + snprintf(errbuf, errbuflen, "Cannot get a new SSL context: %s", ERR_error_string(ERR_get_error(), NULL)); + goto die; + } + + SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); + + if (is_server) + { + char const *certfile = ssl_certfile[0] ? ssl_certfile : "cert.pem"; + if (1 != SSL_CTX_use_certificate_file(ctx, certfile, SSL_FILETYPE_PEM)) + { + snprintf(errbuf, errbuflen, "Cannot read certificate file %s: %s", certfile, ERR_error_string(ERR_get_error(), NULL)); + goto die; + } + + char const *keyfile = ssl_keyfile[0] ? ssl_keyfile : "key.pem"; + if (1 != SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM)) + { + snprintf(errbuf, errbuflen, "Cannot read private key file %s: %s", keyfile, ERR_error_string(ERR_get_error(), NULL)); + goto die; + } + } + else + { + if (ssl_rootfile[0]) + { + if (! SSL_CTX_load_verify_locations(ctx, ssl_rootfile, 0)) + { + snprintf(errbuf, errbuflen, "Cannot read CA list from %s", ssl_rootfile); + goto die; + } + } + else + { + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); + } + } + +#if 0 + if (! RAND_load_file(RANDOM, 1024*1024)) + { + snprintf(errbuf, errbuflen, "Cannot init random"); + goto die; + } + + if (is_server) + { + SSL_CTX_set_session_id_context(ctx, (void *)&s_server_session_id_context, sizeof(s_server_session_id_context)); + } +#endif + + inited = 1; + return 0; + +die: + return -1; +} + +SSL *ssl_promotion(int is_server, SOCKET s, char *errbuf, size_t errbuflen) +{ + if (ssl_init_once(is_server, 1, errbuf, errbuflen) < 0) { + return NULL; + } + + SSL *ssl = SSL_new(ctx); // TODO: also a DTLS context + SSL_set_fd(ssl, (int)s); + + if (is_server) { + if (SSL_accept(ssl) <= 0) { + snprintf(errbuf, errbuflen, "SSL_accept(): %s", + ERR_error_string(ERR_get_error(), NULL)); + return NULL; + } + } else { + if (SSL_connect(ssl) <= 0) { + snprintf(errbuf, errbuflen, "SSL_connect(): %s", + ERR_error_string(ERR_get_error(), NULL)); + return NULL; + } + } + + return ssl; +} + +// Finish using an SSL handle; shut down the connection and free the +// handle. +void ssl_finish(SSL *ssl) +{ + // + // We won't be using this again, so we can just send the + // shutdown alert and free up the handle, and have our + // caller close the socket. + // + // XXX - presumably, if the connection is shut down on + // our side, either our peer won't have a problem sending + // their shutdown alert or will not treat such a problem + // as an error. If this causes errors to be reported, + // fix that as appropriate. + // + SSL_shutdown(ssl); + SSL_free(ssl); +} + +// Same return value as sock_send: +// 0 on OK, -1 on error but closed connection (-2). +int ssl_send(SSL *ssl, char const *buffer, int size, char *errbuf, size_t errbuflen) +{ + int status = SSL_write(ssl, buffer, size); + if (status > 0) + { + // "SSL_write() will only return with success, when the complete contents (...) has been written." + return 0; + } + else + { + int ssl_err = SSL_get_error(ssl, status); // TODO: does it pop the error? + if (ssl_err == SSL_ERROR_ZERO_RETURN) + { + return -2; + } + else if (ssl_err == SSL_ERROR_SYSCALL) + { +#ifndef _WIN32 + if (errno == ECONNRESET || errno == EPIPE) return -2; +#endif + } + snprintf(errbuf, errbuflen, "SSL_write(): %s", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } +} + +// Returns the number of bytes read, or -1 on syserror, or -2 on SSL error. +int ssl_recv(SSL *ssl, char *buffer, int size, char *errbuf, size_t errbuflen) +{ + int status = SSL_read(ssl, buffer, size); + if (status <= 0) + { + int ssl_err = SSL_get_error(ssl, status); + if (ssl_err == SSL_ERROR_ZERO_RETURN) + { + return 0; + } + else if (ssl_err == SSL_ERROR_SYSCALL) + { + return -1; + } + else + { + // Should not happen + snprintf(errbuf, errbuflen, "SSL_read(): %s", + ERR_error_string(ERR_get_error(), NULL)); + return -2; + } + } + else + { + return status; + } +} + +#endif // HAVE_OPENSSL diff --git a/sslutils.h b/sslutils.h new file mode 100644 index 000000000000..6316364ecfc2 --- /dev/null +++ b/sslutils.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2002 - 2003 + * NetGroup, Politecnico di Torino (Italy) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Politecnico di Torino nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __SSLUTILS_H__ +#define __SSLUTILS_H__ + +#ifdef HAVE_OPENSSL +#include "pcap/socket.h" // for SOCKET +#include +#include + +/* + * Utility functions + */ + +void ssl_set_certfile(const char *certfile); +void ssl_set_keyfile(const char *keyfile); +int ssl_init_once(int is_server, int enable_compression, char *errbuf, size_t errbuflen); +SSL *ssl_promotion(int is_server, SOCKET s, char *errbuf, size_t errbuflen); +void ssl_finish(SSL *ssl); +int ssl_send(SSL *, char const *buffer, int size, char *errbuf, size_t errbuflen); +int ssl_recv(SSL *, char *buffer, int size, char *errbuf, size_t errbuflen); + +// The SSL parameters are used +#define _U_NOSSL_ + +#else // HAVE_OPENSSL + +// This saves us from a lot of ifdefs: +#define SSL void const + +// The SSL parameters are unused +#define _U_NOSSL_ _U_ + +#endif // HAVE_OPENSSL + +#endif // __SSLUTILS_H__ diff --git a/testprogs/CMakeLists.txt b/testprogs/CMakeLists.txt index b8ef9b7d0510..567f42aa6ed7 100644 --- a/testprogs/CMakeLists.txt +++ b/testprogs/CMakeLists.txt @@ -19,6 +19,10 @@ macro(add_test_executable _executable) target_link_libraries(${_executable} ${ARGN} ${LIBRARY_NAME}_static ${PCAP_LINK_LIBRARIES}) endif(WIN32) + if(NOT "${LINKER_FLAGS}" STREQUAL "") + set_target_properties(${_executable} PROPERTIES + LINK_FLAGS "${LINKER_FLAGS}") + endif() add_dependencies(testprogs ${_executable}) endmacro() @@ -26,8 +30,10 @@ add_test_executable(can_set_rfmon_test) add_test_executable(capturetest) add_test_executable(filtertest) add_test_executable(findalldevstest) +add_test_executable(findalldevstest-perf) add_test_executable(opentest) add_test_executable(reactivatetest) +add_test_executable(writecaptest) if(NOT WIN32) add_test_executable(selpolltest) @@ -35,6 +41,11 @@ endif() add_test_executable(threadsignaltest ${CMAKE_THREAD_LIBS_INIT}) -if(NOT WIN32) +# Same as in configure.ac. +if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "Darwin" OR + CMAKE_SYSTEM_NAME STREQUAL "Linux") add_test_executable(valgrindtest) endif() + +add_subdirectory(fuzz) diff --git a/testprogs/Makefile.in b/testprogs/Makefile.in index ec0a47208464..f195693713f9 100644 --- a/testprogs/Makefile.in +++ b/testprogs/Makefile.in @@ -38,6 +38,7 @@ mandir = @mandir@ # VPATH srcdir = @srcdir@ +top_srcdir = @top_srcdir@ VPATH = @srcdir@ # @@ -62,7 +63,6 @@ LDFLAGS = @LDFLAGS@ ${CROSSFLAGS} DYEXT = @DYEXT@ V_RPATH_OPT = @V_RPATH_OPT@ DEPENDENCY_CFLAG = @DEPENDENCY_CFLAG@ -EXTRA_NETWORK_LIBS=@EXTRA_NETWORK_LIBS@ # Standard CFLAGS for building test programs FULL_CFLAGS = $(CCOPT) $(INCLS) $(DEFS) $(CFLAGS) @@ -79,14 +79,17 @@ INSTALL_DATA = @INSTALL_DATA@ $(CC) $(FULL_CFLAGS) -c $(srcdir)/$*.c SRC = @VALGRINDTEST_SRC@ \ - capturetest.c \ can_set_rfmon_test.c \ + capturetest.c \ filtertest.c \ + findalldevstest-perf.c \ findalldevstest.c \ opentest.c \ + nonblocktest.c \ reactivatetest.c \ selpolltest.c \ - threadsignaltest.c + threadsignaltest.c \ + writecaptest.c TESTS = $(SRC:.c=) @@ -98,31 +101,56 @@ CLEANFILES = $(OBJ) $(TESTS) all: $(TESTS) capturetest: $(srcdir)/capturetest.c ../libpcap.a - $(CC) $(FULL_CFLAGS) -I. -L. -o capturetest $(srcdir)/capturetest.c ../libpcap.a $(LIBS) + $(CC) $(FULL_CFLAGS) -I. -L. -o capturetest $(srcdir)/capturetest.c \ + ../libpcap.a $(LIBS) can_set_rfmon_test: $(srcdir)/can_set_rfmon_test.c ../libpcap.a - $(CC) $(FULL_CFLAGS) -I. -L. -o can_set_rfmon_test $(srcdir)/can_set_rfmon_test.c ../libpcap.a $(LIBS) + $(CC) $(FULL_CFLAGS) -I. -L. -o can_set_rfmon_test \ + $(srcdir)/can_set_rfmon_test.c \ + ../libpcap.a $(LIBS) filtertest: $(srcdir)/filtertest.c ../libpcap.a - $(CC) $(FULL_CFLAGS) -I. -L. -o filtertest $(srcdir)/filtertest.c ../libpcap.a $(EXTRA_NETWORK_LIBS) $(LIBS) + $(CC) $(FULL_CFLAGS) -I. -L. -o filtertest $(srcdir)/filtertest.c \ + ../libpcap.a $(LIBS) findalldevstest: $(srcdir)/findalldevstest.c ../libpcap.a - $(CC) $(FULL_CFLAGS) -I. -L. -o findalldevstest $(srcdir)/findalldevstest.c ../libpcap.a $(EXTRA_NETWORK_LIBS) $(LIBS) + $(CC) $(FULL_CFLAGS) -I. -L. -o findalldevstest \ + $(srcdir)/findalldevstest.c \ + ../libpcap.a $(LIBS) + +findalldevstest-perf: $(srcdir)/findalldevstest-perf.c ../libpcap.a + $(CC) $(FULL_CFLAGS) -I. -L. -o findalldevstest-perf \ + $(srcdir)/findalldevstest-perf.c \ + ../libpcap.a $(LIBS) opentest: $(srcdir)/opentest.c ../libpcap.a - $(CC) $(FULL_CFLAGS) -I. -L. -o opentest $(srcdir)/opentest.c ../libpcap.a $(LIBS) + $(CC) $(FULL_CFLAGS) -I. -L. -o opentest $(srcdir)/opentest.c \ + ../libpcap.a $(LIBS) + +nonblocktest: $(srcdir)/nonblocktest.c ../libpcap.a + $(CC) $(FULL_CFLAGS) -I. -L. -o nonblocktest $(srcdir)/nonblocktest.c \ + ../libpcap.a $(LIBS) reactivatetest: $(srcdir)/reactivatetest.c ../libpcap.a - $(CC) $(FULL_CFLAGS) -I. -L. -o reactivatetest $(srcdir)/reactivatetest.c ../libpcap.a $(LIBS) + $(CC) $(FULL_CFLAGS) -I. -L. -o reactivatetest \ + $(srcdir)/reactivatetest.c ../libpcap.a $(LIBS) selpolltest: $(srcdir)/selpolltest.c ../libpcap.a - $(CC) $(FULL_CFLAGS) -I. -L. -o selpolltest $(srcdir)/selpolltest.c ../libpcap.a $(LIBS) + $(CC) $(FULL_CFLAGS) -I. -L. -o selpolltest $(srcdir)/selpolltest.c \ + ../libpcap.a $(LIBS) threadsignaltest: $(srcdir)/threadsignaltest.c ../libpcap.a - $(CC) $(FULL_CFLAGS) -I. -L. -o threadsignaltest $(srcdir)/threadsignaltest.c ../libpcap.a $(LIBS) $(PTHREAD_LIBS) + $(CC) $(FULL_CFLAGS) -I. -L. -o threadsignaltest \ + $(srcdir)/threadsignaltest.c \ + ../libpcap.a $(LIBS) $(PTHREAD_LIBS) valgrindtest: $(srcdir)/valgrindtest.c ../libpcap.a - $(CC) $(FULL_CFLAGS) -I. -L. -o valgrindtest $(srcdir)/valgrindtest.c ../libpcap.a $(LIBS) + $(CC) $(FULL_CFLAGS) -I. -L. -o valgrindtest $(srcdir)/valgrindtest.c \ + ../libpcap.a $(LIBS) + +writecaptest: $(srcdir)/writecaptest.c ../libpcap.a + $(CC) $(FULL_CFLAGS) -I. -L. -o writecaptest $(srcdir)/writecaptest.c \ + ../libpcap.a $(LIBS) clean: rm -f $(CLEANFILES) @@ -141,4 +169,4 @@ tags: $(TAGFILES) ctags -wtd $(TAGFILES) depend: - ../$(MKDEP) -c "$(CC)" -m "$(DEPENDENCY_CFLAG)" $(CFLAGS) $(DEFS) $(INCLS) $(SRC) + $(MKDEP) -c "$(CC)" -m "$(DEPENDENCY_CFLAG)" -s "$(srcdir)" $(CFLAGS) $(DEFS) $(INCLS) $(SRC) diff --git a/testprogs/can_set_rfmon_test.c b/testprogs/can_set_rfmon_test.c index f6188ba1399e..96816f9b977c 100644 --- a/testprogs/can_set_rfmon_test.c +++ b/testprogs/can_set_rfmon_test.c @@ -70,7 +70,6 @@ main(int argc, char **argv) else error("%s: pcap_can_set_rfmon failed: %s", argv[1], pcap_statustostr(status)); - return 1; } printf("%s: Monitor mode %s be set\n", argv[1], status ? "can" : "cannot"); return 0; diff --git a/testprogs/capturetest.c b/testprogs/capturetest.c index d625cb4ab2b7..4eadd3367968 100644 --- a/testprogs/capturetest.c +++ b/testprogs/capturetest.c @@ -38,6 +38,9 @@ The Regents of the University of California. All rights reserved.\n"; #include #endif #include +#ifndef _WIN32 + #include +#endif #include #include @@ -58,6 +61,37 @@ static void warning(const char *, ...) PCAP_PRINTFLIKE(1, 2); static char *copy_argv(char **); static pcap_t *pd; +#ifndef _WIN32 +static int breaksigint = 0; +#endif + +#ifndef _WIN32 +static void +sigint_handler(int signum _U_) +{ + if (breaksigint) + pcap_breakloop(pd); +} +#endif + +#ifdef _WIN32 +/* + * We don't have UN*X-style signals, so we don't have anything to test. + */ +#define B_OPTION "" +#define R_OPTION "" +#define S_OPTION "" +#else +/* + * We do have UN*X-style signals (we assume that "not Windows" means "UN*X"). + */ +#define B_OPTION "b" +#define R_OPTION "r" +#define S_OPTION "s" +#endif + +#define COMMAND_OPTIONS B_OPTION "i:mn" R_OPTION S_OPTION "t:" +#define USAGE_OPTIONS "-" B_OPTION "mn" R_OPTION S_OPTION int main(int argc, char **argv) @@ -69,6 +103,10 @@ main(int argc, char **argv) int timeout = 1000; int immediate = 0; int nonblock = 0; +#ifndef _WIN32 + int sigrestart = 0; + int catchsigint = 0; +#endif pcap_if_t *devlist; bpf_u_int32 localnet, netmask; struct bpf_program fcode; @@ -83,9 +121,15 @@ main(int argc, char **argv) program_name = argv[0]; opterr = 0; - while ((op = getopt(argc, argv, "i:mnt:")) != -1) { + while ((op = getopt(argc, argv, COMMAND_OPTIONS)) != -1) { switch (op) { +#ifndef _WIN32 + case 'b': + breaksigint = 1; + break; +#endif + case 'i': device = optarg; break; @@ -98,6 +142,16 @@ main(int argc, char **argv) nonblock = 1; break; +#ifndef _WIN32 + case 'r': + sigrestart = 1; + break; + + case 's': + catchsigint = 1; + break; +#endif + case 't': longarg = strtol(optarg, &p, 10); if (p == optarg || *p != '\0') { @@ -132,6 +186,28 @@ main(int argc, char **argv) pcap_freealldevs(devlist); } *ebuf = '\0'; + +#ifndef _WIN32 + /* + * If we were told to catch SIGINT, do so. + */ + if (catchsigint) { + struct sigaction action; + + action.sa_handler = sigint_handler; + sigemptyset(&action.sa_mask); + + /* + * Should SIGINT interrupt, or restart, system calls? + */ + action.sa_flags = sigrestart ? SA_RESTART : 0; + + if (sigaction(SIGINT, &action, NULL) == -1) + error("Can't catch SIGINT: %s\n", + strerror(errno)); + } +#endif + pd = pcap_create(device, ebuf); if (pd == NULL) error("%s", ebuf); @@ -188,6 +264,10 @@ main(int argc, char **argv) if (status != 0) { printf("%d packets seen, %d packets counted after pcap_dispatch returns\n", status, packet_count); + struct pcap_stat ps; + pcap_stats(pd, &ps); + printf("%d ps_recv, %d ps_drop, %d ps_ifdrop\n", + ps.ps_recv, ps.ps_drop, ps.ps_ifdrop); } } if (status == -2) { @@ -197,13 +277,14 @@ main(int argc, char **argv) * Print an extra newline, just in case. */ putchar('\n'); + printf("Broken out of loop from SIGINT handler\n"); } (void)fflush(stdout); if (status == -1) { /* * Error. Report it. */ - (void)fprintf(stderr, "%s: pcap_loop: %s\n", + (void)fprintf(stderr, "%s: pcap_dispatch: %s\n", program_name, pcap_geterr(pd)); } pcap_close(pd); @@ -223,7 +304,7 @@ countme(u_char *user, const struct pcap_pkthdr *h _U_, const u_char *sp _U_) static void usage(void) { - (void)fprintf(stderr, "Usage: %s [ -mn ] [ -i interface ] [ -t timeout] [expression]\n", + (void)fprintf(stderr, "Usage: %s [ " USAGE_OPTIONS " ] [ -i interface ] [ -t timeout] [expression]\n", program_name); exit(1); } @@ -271,7 +352,7 @@ static char * copy_argv(register char **argv) { register char **p; - register u_int len = 0; + register size_t len = 0; char *buf; char *src, *dst; diff --git a/testprogs/filtertest.c b/testprogs/filtertest.c index 7e2d6d6e186d..15556d045cf7 100644 --- a/testprogs/filtertest.c +++ b/testprogs/filtertest.c @@ -36,6 +36,7 @@ The Regents of the University of California. All rights reserved.\n"; #include #include #include +#include #ifdef _WIN32 #include "getopt.h" #include "unix.h" @@ -56,6 +57,8 @@ The Regents of the University of California. All rights reserved.\n"; #include "pcap/funcattrs.h" +#define MAXIMUM_SNAPLEN 262144 + #ifdef BDEBUG /* * We have pcap_set_optimizer_debug() and pcap_set_print_dot_graph() in @@ -99,11 +102,21 @@ read_infile(char *fname) if (fstat(fd, &buf) < 0) error("can't stat %s: %s", fname, pcap_strerror(errno)); + /* + * _read(), on Windows, has an unsigned int byte count and an + * int return value, so we can't handle a file bigger than + * INT_MAX - 1 bytes (and have no reason to do so; a filter *that* + * big will take forever to compile). (The -1 is for the '\0' at + * the end of the string.) + */ + if (buf.st_size > INT_MAX - 1) + error("%s is larger than %d bytes; that's too large", fname, + INT_MAX - 1); cp = malloc((u_int)buf.st_size + 1); if (cp == NULL) error("malloc(%d) for %s: %s", (u_int)buf.st_size + 1, fname, pcap_strerror(errno)); - cc = read(fd, cp, (u_int)buf.st_size); + cc = (int)read(fd, cp, (u_int)buf.st_size); if (cc < 0) error("read %s: %s", fname, pcap_strerror(errno)); if (cc != buf.st_size) @@ -163,7 +176,7 @@ static char * copy_argv(register char **argv) { register char **p; - register u_int len = 0; + register size_t len = 0; char *buf; char *src, *dst; @@ -196,13 +209,14 @@ main(int argc, char **argv) char *cp; int op; int dflag; +#ifdef BDEBUG int gflag; +#endif char *infile; int Oflag; - long snaplen; + int snaplen; char *p; int dlt; - int have_fcode = 0; bpf_u_int32 netmask = PCAP_NETMASK_UNKNOWN; char *cmdbuf; pcap_t *pd; @@ -214,11 +228,13 @@ main(int argc, char **argv) #endif /* _WIN32 */ dflag = 1; +#ifdef BDEBUG gflag = 0; +#endif infile = NULL; Oflag = 1; - snaplen = 68; + snaplen = MAXIMUM_SNAPLEN; if ((cp = strrchr(argv[0], '/')) != NULL) program_name = cp + 1; @@ -272,13 +288,19 @@ main(int argc, char **argv) case 's': { char *end; + long long_snaplen; - snaplen = strtol(optarg, &end, 0); + long_snaplen = strtol(optarg, &end, 0); if (optarg == end || *end != '\0' - || snaplen < 0 || snaplen > 65535) + || long_snaplen < 0 + || long_snaplen > MAXIMUM_SNAPLEN) error("invalid snaplen %s", optarg); - else if (snaplen == 0) - snaplen = 65535; + else { + if (snaplen == 0) + snaplen = MAXIMUM_SNAPLEN; + else + snaplen = (int)long_snaplen; + } break; } @@ -317,7 +339,6 @@ main(int argc, char **argv) if (pcap_compile(pd, &fcode, cmdbuf, Oflag, netmask) < 0) error("%s", pcap_geterr(pd)); - have_fcode = 1; if (!bpf_validate(fcode.bf_insns, fcode.bf_len)) warn("Filter doesn't pass validation"); @@ -337,8 +358,7 @@ main(int argc, char **argv) bpf_dump(&fcode, dflag); free(cmdbuf); - if (have_fcode) - pcap_freecode (&fcode); + pcap_freecode (&fcode); pcap_close(pd); exit(0); } diff --git a/testprogs/findalldevstest-perf.c b/testprogs/findalldevstest-perf.c new file mode 100644 index 000000000000..16f53cdc5967 --- /dev/null +++ b/testprogs/findalldevstest-perf.c @@ -0,0 +1,97 @@ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#ifdef _WIN32 + #include + #include + #include +#else + #include + #include +#endif + +#include + +#include "varattrs.h" +#include "pcap/funcattrs.h" +#include "portability.h" + +int main(int argc _U_, char **argv _U_) +{ + pcap_if_t *alldevs; + int exit_status = 0; + char errbuf[PCAP_ERRBUF_SIZE+1]; +#ifdef _WIN32 + FILETIME start_ktime, start_utime, end_ktime, end_utime; + FILETIME dummy1, dummy2; + ULARGE_INTEGER start_kticks, end_kticks, start_uticks, end_uticks; + ULONGLONG ktime, utime, tottime; +#else + struct rusage start_rusage, end_rusage; + struct timeval ktime, utime, tottime; +#endif + +#ifdef _WIN32 + if (!GetProcessTimes(GetCurrentProcess(), &dummy1, &dummy2, + &start_ktime, &start_utime)) + { + fprintf(stderr, "GetProcessTimes() fails at start\n"); + exit(1); + } + start_kticks.LowPart = start_ktime.dwLowDateTime; + start_kticks.HighPart = start_ktime.dwHighDateTime; + start_uticks.LowPart = start_utime.dwLowDateTime; + start_uticks.HighPart = start_utime.dwHighDateTime; +#else + if (getrusage(RUSAGE_SELF, &start_rusage) == -1) { + fprintf(stderr, "getrusage() fails at start\n"); + exit(1); + } +#endif + for (int i = 0; i < 500; i++) + { + if (pcap_findalldevs(&alldevs, errbuf) == -1) + { + fprintf(stderr,"Error in pcap_findalldevs: %s\n",errbuf); + exit(1); + } + pcap_freealldevs(alldevs); + } + +#ifdef _WIN32 + if (!GetProcessTimes(GetCurrentProcess(), &dummy1, &dummy2, + &end_ktime, &end_utime)) + { + fprintf(stderr, "GetProcessTimes() fails at end\n"); + exit(1); + } + end_kticks.LowPart = end_ktime.dwLowDateTime; + end_kticks.HighPart = end_ktime.dwHighDateTime; + end_uticks.LowPart = end_utime.dwLowDateTime; + end_uticks.HighPart = end_utime.dwHighDateTime; + ktime = end_kticks.QuadPart - start_kticks.QuadPart; + utime = end_uticks.QuadPart - start_uticks.QuadPart; + tottime = ktime + utime; + printf("Total CPU secs: kernel %g, user %g, total %g\n", + ((double)ktime) / 10000000.0, + ((double)utime) / 10000000.0, + ((double)tottime) / 10000000.0); +#else + if (getrusage(RUSAGE_SELF, &end_rusage) == -1) { + fprintf(stderr, "getrusage() fails at end\n"); + exit(1); + } + timersub(&end_rusage.ru_stime, &start_rusage.ru_stime, &ktime); + timersub(&end_rusage.ru_utime, &start_rusage.ru_utime, &utime); + timeradd(&ktime, &utime, &tottime); + printf("Total CPU secs: kernel %g, user %g, total %g\n", + (double)ktime.tv_sec + ((double)ktime.tv_usec / 1000000.0), + (double)utime.tv_sec + ((double)utime.tv_usec / 1000000.0), + (double)tottime.tv_sec + ((double)tottime.tv_usec / 1000000.0)); +#endif + exit(exit_status); +} diff --git a/testprogs/findalldevstest.c b/testprogs/findalldevstest.c index e535e25462fb..062932090381 100644 --- a/testprogs/findalldevstest.c +++ b/testprogs/findalldevstest.c @@ -19,6 +19,7 @@ #include +#include "varattrs.h" #include "pcap/funcattrs.h" static int ifprint(pcap_if_t *d); @@ -94,7 +95,11 @@ getpass(const char *prompt) } #endif +#ifdef ENABLE_REMOTE int main(int argc, char **argv) +#else +int main(int argc _U_, char **argv _U_) +#endif { pcap_if_t *alldevs; pcap_if_t *d; @@ -152,8 +157,24 @@ int main(int argc, char **argv) { if (pcap_lookupnet(alldevs->name, &net, &mask, errbuf) < 0) { - fprintf(stderr,"Error in pcap_lookupnet: %s\n",errbuf); - exit_status = 2; + /* + * XXX - this doesn't distinguish between "a real error + * occurred" and "this interface doesn't *have* an IPv4 + * address". The latter shouldn't be treated as an error. + * + * We look for the interface name, followed by a colon and + * a space, and, if we find it,w e see if what follows it + * is "no IPv4 address assigned". + */ + size_t devnamelen = strlen(alldevs->name); + if (strncmp(errbuf, alldevs->name, devnamelen) == 0 && + strncmp(errbuf + devnamelen, ": ", 2) == 0 && + strcmp(errbuf + devnamelen + 2, "no IPv4 address assigned") == 0) + printf("Preferred device is not on an IPv4 network\n"); + else { + fprintf(stderr,"Error in pcap_lookupnet: %s\n",errbuf); + exit_status = 2; + } } else { @@ -300,12 +321,12 @@ static int ifprint(pcap_if_t *d) #define IPTOSBUFFERS 12 static char *iptos(bpf_u_int32 in) { - static char output[IPTOSBUFFERS][3*4+3+1]; + static char output[IPTOSBUFFERS][sizeof("255.255.255.255")]; static short which; u_char *p; p = (u_char *)∈ which = (which + 1 == IPTOSBUFFERS ? 0 : which + 1); - sprintf(output[which], "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + snprintf(output[which], sizeof(output[which]), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); return output[which]; } diff --git a/testprogs/fuzz/CMakeLists.txt b/testprogs/fuzz/CMakeLists.txt new file mode 100644 index 000000000000..67250cca4349 --- /dev/null +++ b/testprogs/fuzz/CMakeLists.txt @@ -0,0 +1,43 @@ +add_executable(fuzz_pcap onefile.c fuzz_pcap.c) +target_link_libraries(fuzz_pcap ${ARGN} ${LIBRARY_NAME}_static ${PCAP_LINK_LIBRARIES}) +if(NOT "${SANITIZER_FLAGS}" STREQUAL "") + set_target_properties(fuzz_pcap PROPERTIES + LINK_FLAGS "${SANITIZER_FLAGS}") +endif() + +add_executable(fuzz_filter onefile.c fuzz_filter.c) +target_link_libraries(fuzz_filter ${ARGN} ${LIBRARY_NAME}_static ${PCAP_LINK_LIBRARIES}) +if(NOT "${SANITIZER_FLAGS}" STREQUAL "") + set_target_properties(fuzz_filter PROPERTIES + LINK_FLAGS "${SANITIZER_FLAGS}") +endif() + +add_executable(fuzz_both onefile.c fuzz_both.c) +target_link_libraries(fuzz_both ${ARGN} ${LIBRARY_NAME}_static ${PCAP_LINK_LIBRARIES}) +if(NOT "${SANITIZER_FLAGS}" STREQUAL "") + set_target_properties(fuzz_both PROPERTIES + LINK_FLAGS "${SANITIZER_FLAGS}") +endif() + +if(ENABLE_REMOTE AND "$ENV{CFLAGS}" MATCHES "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION") +add_executable(fuzz_rclient onefile.c fuzz_rclient.c) +target_link_libraries(fuzz_rclient ${ARGN} ${LIBRARY_NAME}_static ${PCAP_LINK_LIBRARIES}) +if(NOT "${SANITIZER_FLAGS}" STREQUAL "") + set_target_properties(fuzz_rclient PROPERTIES + LINK_FLAGS "${SANITIZER_FLAGS}") +endif() + +add_executable(fuzz_rserver onefile.c fuzz_rserver.c ../../rpcapd/daemon.c) +check_function_exists(crypt HAVE_CRYPT_IN_SYSTEM_LIBRARIES) +if(HAVE_CRYPT_IN_SYSTEM_LIBRARIES) + set(HAVE_CRYPT TRUE) +else(HAVE_CRYPT_IN_SYSTEM_LIBRARIES) + set(PCAP_LINK_LIBRARIES ${PCAP_LINK_LIBRARIES} crypt) +endif(HAVE_CRYPT_IN_SYSTEM_LIBRARIES) +target_link_libraries(fuzz_rserver ${ARGN} ${LIBRARY_NAME}_static ${PCAP_LINK_LIBRARIES}) + +if(NOT "${SANITIZER_FLAGS}" STREQUAL "") + set_target_properties(fuzz_rserver PROPERTIES + LINK_FLAGS "${SANITIZER_FLAGS}") +endif() +endif(ENABLE_REMOTE AND "$ENV{CFLAGS}" MATCHES "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION") diff --git a/testprogs/fuzz/fuzz_both.c b/testprogs/fuzz/fuzz_both.c new file mode 100644 index 000000000000..59e3d404103d --- /dev/null +++ b/testprogs/fuzz/fuzz_both.c @@ -0,0 +1,101 @@ +#include +#include +#include +#include +#include + +#include + +FILE * outfile = NULL; + +static int bufferToFile(const char * name, const uint8_t *Data, size_t Size) { + FILE * fd; + if (remove(name) != 0) { + if (errno != ENOENT) { + printf("failed remove, errno=%d\n", errno); + return -1; + } + } + fd = fopen(name, "wb"); + if (fd == NULL) { + printf("failed open, errno=%d\n", errno); + return -2; + } + if (fwrite (Data, 1, Size, fd) != Size) { + fclose(fd); + return -3; + } + fclose(fd); + return 0; +} + +void fuzz_openFile(const char * name) { + if (outfile != NULL) { + fclose(outfile); + } + outfile = fopen(name, "w"); +} + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + pcap_t * pkts; + char errbuf[PCAP_ERRBUF_SIZE]; + const u_char *pkt; + struct pcap_pkthdr *header; + int r; + size_t filterSize; + char * filter; + struct bpf_program bpf; + + + //initialize output file + if (outfile == NULL) { + outfile = fopen("/dev/null", "w"); + if (outfile == NULL) { + return 0; + } + } + + if (Size < 1) { + return 0; + } + filterSize = Data[0]; + if (Size < 1+filterSize || filterSize == 0) { + return 0; + } + + //rewrite buffer to a file as libpcap does not have buffer inputs + if (bufferToFile("/tmp/fuzz.pcap", Data+1+filterSize, Size-(1+filterSize)) < 0) { + return 0; + } + + //initialize structure + pkts = pcap_open_offline("/tmp/fuzz.pcap", errbuf); + if (pkts == NULL) { + fprintf(outfile, "Couldn't open pcap file %s\n", errbuf); + return 0; + } + + filter = malloc(filterSize); + memcpy(filter, Data+1, filterSize); + //null terminate string + filter[filterSize-1] = 0; + + if (pcap_compile(pkts, &bpf, filter, 1, PCAP_NETMASK_UNKNOWN) == 0) { + //loop over packets + r = pcap_next_ex(pkts, &header, &pkt); + while (r > 0) { + //checks filter + fprintf(outfile, "packet length=%d/%d filter=%d\n",header->caplen, header->len, pcap_offline_filter(&bpf, header, pkt)); + r = pcap_next_ex(pkts, &header, &pkt); + } + //close structure + pcap_close(pkts); + pcap_freecode(&bpf); + } + else { + pcap_close(pkts); + } + free(filter); + + return 0; +} diff --git a/testprogs/fuzz/fuzz_both.options b/testprogs/fuzz/fuzz_both.options new file mode 100644 index 000000000000..0824b19fab47 --- /dev/null +++ b/testprogs/fuzz/fuzz_both.options @@ -0,0 +1,2 @@ +[libfuzzer] +max_len = 65535 diff --git a/testprogs/fuzz/fuzz_filter.c b/testprogs/fuzz/fuzz_filter.c new file mode 100644 index 000000000000..de350672797f --- /dev/null +++ b/testprogs/fuzz/fuzz_filter.c @@ -0,0 +1,43 @@ +#include +#include +#include + +#include + +void fuzz_openFile(const char * name){ + //do nothing +} + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + pcap_t * pkts; + struct bpf_program bpf; + char * filter; + + //we need at least 1 byte for linktype + if (Size < 1) { + return 0; + } + + //initialize structure snaplen = 65535 + pkts = pcap_open_dead(Data[Size-1], 0xFFFF); + if (pkts == NULL) { + printf("pcap_open_dead failed\n"); + return 0; + } + filter = malloc(Size); + memcpy(filter, Data, Size); + //null terminate string + filter[Size-1] = 0; + + if (pcap_compile(pkts, &bpf, filter, 1, PCAP_NETMASK_UNKNOWN) == 0) { + pcap_setfilter(pkts, &bpf); + pcap_close(pkts); + pcap_freecode(&bpf); + } + else { + pcap_close(pkts); + } + free(filter); + + return 0; +} diff --git a/testprogs/fuzz/fuzz_filter.options b/testprogs/fuzz/fuzz_filter.options new file mode 100644 index 000000000000..9fda93fcb340 --- /dev/null +++ b/testprogs/fuzz/fuzz_filter.options @@ -0,0 +1,2 @@ +[libfuzzer] +max_len = 4096 diff --git a/testprogs/fuzz/fuzz_pcap.c b/testprogs/fuzz/fuzz_pcap.c new file mode 100644 index 000000000000..fba5312fcaf2 --- /dev/null +++ b/testprogs/fuzz/fuzz_pcap.c @@ -0,0 +1,80 @@ +#include +#include +#include +#include + +#include + +FILE * outfile = NULL; + +static int bufferToFile(const char * name, const uint8_t *Data, size_t Size) { + FILE * fd; + if (remove(name) != 0) { + if (errno != ENOENT) { + printf("failed remove, errno=%d\n", errno); + return -1; + } + } + fd = fopen(name, "wb"); + if (fd == NULL) { + printf("failed open, errno=%d\n", errno); + return -2; + } + if (fwrite (Data, 1, Size, fd) != Size) { + fclose(fd); + return -3; + } + fclose(fd); + return 0; +} + +void fuzz_openFile(const char * name) { + if (outfile != NULL) { + fclose(outfile); + } + outfile = fopen(name, "w"); +} + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + pcap_t * pkts; + char errbuf[PCAP_ERRBUF_SIZE]; + const u_char *pkt; + struct pcap_pkthdr *header; + struct pcap_stat stats; + int r; + + //initialize output file + if (outfile == NULL) { + outfile = fopen("/dev/null", "w"); + if (outfile == NULL) { + return 0; + } + } + + //rewrite buffer to a file as libpcap does not have buffer inputs + if (bufferToFile("/tmp/fuzz.pcap", Data, Size) < 0) { + return 0; + } + + //initialize structure + pkts = pcap_open_offline("/tmp/fuzz.pcap", errbuf); + if (pkts == NULL) { + fprintf(outfile, "Couldn't open pcap file %s\n", errbuf); + return 0; + } + + //loop over packets + r = pcap_next_ex(pkts, &header, &pkt); + while (r > 0) { + //TODO pcap_offline_filter + fprintf(outfile, "packet length=%d/%d\n",header->caplen, header->len); + r = pcap_next_ex(pkts, &header, &pkt); + } + if (pcap_stats(pkts, &stats) == 0) { + fprintf(outfile, "number of packets=%d\n", stats.ps_recv); + } + //close structure + pcap_close(pkts); + + return 0; +} diff --git a/testprogs/fuzz/fuzz_pcap.options b/testprogs/fuzz/fuzz_pcap.options new file mode 100644 index 000000000000..0824b19fab47 --- /dev/null +++ b/testprogs/fuzz/fuzz_pcap.options @@ -0,0 +1,2 @@ +[libfuzzer] +max_len = 65535 diff --git a/testprogs/fuzz/onefile.c b/testprogs/fuzz/onefile.c new file mode 100644 index 000000000000..690a63bdc433 --- /dev/null +++ b/testprogs/fuzz/onefile.c @@ -0,0 +1,54 @@ +#include +#include +#include + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); +void fuzz_openFile(const char * name); + +int main(int argc, char** argv) +{ + FILE * fp; + uint8_t *Data; + size_t Size; + + if (argc == 3) { + fuzz_openFile(argv[2]); + } else if (argc != 2) { + return 1; + } + //opens the file, get its size, and reads it into a buffer + fp = fopen(argv[1], "rb"); + if (fp == NULL) { + return 2; + } + if (fseek(fp, 0L, SEEK_END) != 0) { + fclose(fp); + return 2; + } + Size = ftell(fp); + if (Size == (size_t) -1) { + fclose(fp); + return 2; + } + if (fseek(fp, 0L, SEEK_SET) != 0) { + fclose(fp); + return 2; + } + Data = malloc(Size); + if (Data == NULL) { + fclose(fp); + return 2; + } + if (fread(Data, Size, 1, fp) != 1) { + fclose(fp); + free(Data); + return 2; + } + + //launch fuzzer + LLVMFuzzerTestOneInput(Data, Size); + free(Data); + fclose(fp); + return 0; +} + diff --git a/testprogs/nonblocktest.c b/testprogs/nonblocktest.c new file mode 100644 index 000000000000..72700a3b64a1 --- /dev/null +++ b/testprogs/nonblocktest.c @@ -0,0 +1,187 @@ +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "varattrs.h" + +/* + * Tests for pcap_set_nonblock / pcap_get_nonblock: + * - idempotency + * - set/get are symmetric + * - get returns the same before/after activate + * - pcap_breakloop works after setting nonblock on and then off + * + * Really this is meant to + * be run manually under strace, to check for extra + * calls to eventfd or close. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static pcap_t *pd; +static char *program_name = "nonblocktest"; +/* Forwards */ +static void PCAP_NORETURN usage(void); +static void PCAP_NORETURN error(const char *, ...) PCAP_PRINTFLIKE(1, 2); +static void warning(const char *, ...) PCAP_PRINTFLIKE(1, 2); + +/* VARARGS */ +static void +error(const char *fmt, ...) +{ + va_list ap; + + (void)fprintf(stderr, "%s: ", program_name); + va_start(ap, fmt); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + if (*fmt) { + fmt += strlen(fmt); + if (fmt[-1] != '\n') + (void)fputc('\n', stderr); + } + exit(1); + /* NOTREACHED */ +} + +/* VARARGS */ +static void +warning(const char *fmt, ...) +{ + va_list ap; + + (void)fprintf(stderr, "%s: WARNING: ", program_name); + va_start(ap, fmt); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + if (*fmt) { + fmt += strlen(fmt); + if (fmt[-1] != '\n') + (void)fputc('\n', stderr); + } +} + +static void +usage(void) +{ + (void)fprintf(stderr, "Usage: %s [ -i interface ]\n", + program_name); + exit(1); +} + +static void +breakme(u_char *user _U_, const struct pcap_pkthdr *h _U_, const u_char *sp _U_) +{ + warning("using pcap_breakloop()"); + pcap_breakloop(pd); +} + +int +main(int argc, char **argv) +{ + int status, op, i, ret; + char *device; + pcap_if_t *devlist; + char ebuf[PCAP_ERRBUF_SIZE]; + + device = NULL; + while ((op = getopt(argc, argv, "i:sptnq")) != -1) { + switch (op) { + + case 'i': + device = optarg; + break; + + default: + usage(); + /* NOTREACHED */ + } + } + if (device == NULL) { + if (pcap_findalldevs(&devlist, ebuf) == -1) + error("%s", ebuf); + if (devlist == NULL) + error("no interfaces available for capture"); + device = strdup(devlist->name); + warning("listening on %s", device); + pcap_freealldevs(devlist); + } + *ebuf = '\0'; + pd = pcap_create(device, ebuf); + if (pd == NULL) + error("%s", ebuf); + else if (*ebuf) + warning("%s", ebuf); + /* set nonblock before activate */ + if (pcap_setnonblock(pd, 1, ebuf) < 0) + error("pcap_setnonblock failed: %s", ebuf); + /* getnonblock just returns "not activated yet" */ + ret = pcap_getnonblock(pd, ebuf); + if (ret != PCAP_ERROR_NOT_ACTIVATED) + error("pcap_getnonblock unexpectedly succeeded"); + if ((status = pcap_activate(pd)) < 0) + error("pcap_activate failed"); + ret = pcap_getnonblock(pd, ebuf); + if (ret != 1) + error( "pcap_getnonblock did not return nonblocking" ); + + /* Set nonblock multiple times, ensure with strace that it's a noop */ + for (i=0; i<10; i++) { + if (pcap_setnonblock(pd, 1, ebuf) < 0) + error("pcap_setnonblock failed: %s", ebuf); + ret = pcap_getnonblock(pd, ebuf); + if (ret != 1) + error( "pcap_getnonblock did not return nonblocking" ); + } + /* Set block multiple times, ensure with strace that it's a noop */ + for (i=0; i<10; i++) { + if (pcap_setnonblock(pd, 0, ebuf) < 0) + error("pcap_setnonblock failed: %s", ebuf); + ret = pcap_getnonblock(pd, ebuf); + if (ret != 0) + error( "pcap_getnonblock did not return blocking" ); + } + + /* Now pcap_loop forever, with a callback that + * uses pcap_breakloop to get out of forever */ + pcap_loop(pd, -1, breakme, NULL); + + /* Now test that pcap_setnonblock fails if we can't open the + * eventfd. */ + if (pcap_setnonblock(pd, 1, ebuf) < 0) + error("pcap_setnonblock failed: %s", ebuf); + while (1) { + ret = open("/dev/null", O_RDONLY); + if (ret < 0) + break; + } + ret = pcap_setnonblock(pd, 0, ebuf); + if (ret == 0) + error("pcap_setnonblock succeeded even though file table is full"); + else + warning("pcap_setnonblock failed as expected: %s", ebuf); +} diff --git a/testprogs/opentest.c b/testprogs/opentest.c index bad38eb0e9fb..a441dda1268f 100644 --- a/testprogs/opentest.c +++ b/testprogs/opentest.c @@ -45,7 +45,7 @@ The Regents of the University of California. All rights reserved.\n"; #include "portability.h" #endif -#define MAXIMUM_SNAPLEN 65535 +#define MAXIMUM_SNAPLEN 262144 static char *program_name; @@ -81,7 +81,7 @@ main(int argc, char **argv) switch (op) { case 'i': - device = optarg; + device = strdup(optarg); break; case 'I': @@ -95,13 +95,19 @@ main(int argc, char **argv) case 's': { char *end; + long long_snaplen; - snaplen = strtol(optarg, &end, 0); + long_snaplen = strtol(optarg, &end, 0); if (optarg == end || *end != '\0' - || snaplen < 0 || snaplen > MAXIMUM_SNAPLEN) + || long_snaplen < 0 + || long_snaplen > MAXIMUM_SNAPLEN) error("invalid snaplen %s", optarg); - else if (snaplen == 0) - snaplen = MAXIMUM_SNAPLEN; + else { + if (snaplen == 0) + snaplen = MAXIMUM_SNAPLEN; + else + snaplen = (int)long_snaplen; + } break; } @@ -186,6 +192,7 @@ main(int argc, char **argv) else printf("%s opened successfully\n", device); } + free(device); pcap_close(pd); exit(status < 0 ? 1 : 0); } diff --git a/testprogs/reactivatetest.c b/testprogs/reactivatetest.c index d7f3e322c175..a9c987aa5015 100644 --- a/testprogs/reactivatetest.c +++ b/testprogs/reactivatetest.c @@ -51,7 +51,6 @@ main(void) if (pd == NULL) { error("Neither lo0 nor lo could be opened: %s", ebuf); - return 2; } } status = pcap_activate(pd); diff --git a/testprogs/selpolltest.c b/testprogs/selpolltest.c index 329281dc24bc..ab7f8f462a0a 100644 --- a/testprogs/selpolltest.c +++ b/testprogs/selpolltest.c @@ -69,13 +69,13 @@ main(int argc, char **argv) register int op; bpf_u_int32 localnet, netmask; register char *cp, *cmdbuf, *device; - int doselect, dopoll, dotimeout, dononblock; + int doselect, dopoll, dotimeout, dononblock, quiet; const char *mechanism; struct bpf_program fcode; char ebuf[PCAP_ERRBUF_SIZE]; pcap_if_t *devlist; - int selectable_fd; - struct timeval *required_timeout; + int selectable_fd = -1; + const struct timeval *required_timeout; int status; int packet_count; @@ -85,13 +85,14 @@ main(int argc, char **argv) mechanism = NULL; dotimeout = 0; dononblock = 0; + quiet = 0; if ((cp = strrchr(argv[0], '/')) != NULL) program_name = cp + 1; else program_name = argv[0]; opterr = 0; - while ((op = getopt(argc, argv, "i:sptn")) != -1) { + while ((op = getopt(argc, argv, "i:sptnq")) != -1) { switch (op) { case 'i': @@ -116,6 +117,10 @@ main(int argc, char **argv) dononblock = 1; break; + case 'q': + quiet = 1; + break; + default: usage(); /* NOTREACHED */ @@ -196,6 +201,7 @@ main(int argc, char **argv) for (;;) { fd_set setread, setexcept; struct timeval seltimeout; + struct timeval *timeoutp; FD_ZERO(&setread); if (selectable_fd != -1) { @@ -203,6 +209,7 @@ main(int argc, char **argv) FD_ZERO(&setexcept); FD_SET(selectable_fd, &setexcept); } + required_timeout = pcap_get_required_select_timeout(pd); if (dotimeout) { seltimeout.tv_sec = 0; if (required_timeout != NULL && @@ -210,37 +217,34 @@ main(int argc, char **argv) seltimeout.tv_usec = required_timeout->tv_usec; else seltimeout.tv_usec = 1000; - status = select(selectable_fd + 1, &setread, - NULL, &setexcept, &seltimeout); + timeoutp = &seltimeout; } else if (required_timeout != NULL) { seltimeout = *required_timeout; - status = select(selectable_fd + 1, &setread, - NULL, &setexcept, &seltimeout); + timeoutp = &seltimeout; } else { - status = select((selectable_fd == -1) ? - 0 : selectable_fd + 1, &setread, - NULL, &setexcept, NULL); + timeoutp = NULL; } + status = select((selectable_fd == -1) ? + 0 : selectable_fd + 1, &setread, NULL, &setexcept, + timeoutp); if (status == -1) { printf("Select returns error (%s)\n", strerror(errno)); } else { - if (selectable_fd == -1) { - if (status != 0) - printf("Select returned a descriptor\n"); - } else { + if (!quiet) { if (status == 0) printf("Select timed out: "); - else + else{ printf("Select returned a descriptor: "); - if (FD_ISSET(selectable_fd, &setread)) - printf("readable, "); - else - printf("not readable, "); - if (FD_ISSET(selectable_fd, &setexcept)) - printf("exceptional condition\n"); - else - printf("no exceptional condition\n"); + if (FD_ISSET(selectable_fd, &setread)) + printf("readable, "); + else + printf("not readable, "); + if (FD_ISSET(selectable_fd, &setexcept)) + printf("exceptional condition\n"); + else + printf("no exceptional condition\n"); + } } packet_count = 0; status = pcap_dispatch(pd, -1, countme, @@ -268,11 +272,12 @@ main(int argc, char **argv) fd.fd = selectable_fd; fd.events = POLLIN; + required_timeout = pcap_get_required_select_timeout(pd); if (dotimeout) polltimeout = 1; else if (required_timeout != NULL && required_timeout->tv_usec >= 1000) - polltimeout = required_timeout->tv_usec/1000; + polltimeout = (int)(required_timeout->tv_usec/1000); else polltimeout = -1; status = poll(&fd, (selectable_fd == -1) ? 0 : 1, polltimeout); @@ -280,10 +285,7 @@ main(int argc, char **argv) printf("Poll returns error (%s)\n", strerror(errno)); } else { - if (selectable_fd == -1) { - if (status != 0) - printf("Poll returned a descriptor\n"); - } else { + if (!quiet) { if (status == 0) printf("Poll timed out\n"); else { @@ -349,7 +351,7 @@ main(int argc, char **argv) /* * Error. Report it. */ - (void)fprintf(stderr, "%s: pcap_loop: %s\n", + (void)fprintf(stderr, "%s: pcap_dispatch: %s\n", program_name, pcap_geterr(pd)); } pcap_close(pd); @@ -367,7 +369,7 @@ countme(u_char *user, const struct pcap_pkthdr *h _U_, const u_char *sp _U_) static void usage(void) { - (void)fprintf(stderr, "Usage: %s [ -sptn ] [ -i interface ] [expression]\n", + (void)fprintf(stderr, "Usage: %s [ -sptnq ] [ -i interface ] [expression]\n", program_name); exit(1); } @@ -415,7 +417,7 @@ static char * copy_argv(register char **argv) { register char **p; - register u_int len = 0; + register size_t len = 0; char *buf; char *src, *dst; diff --git a/testprogs/threadsignaltest.c b/testprogs/threadsignaltest.c index a60bb49523fb..c9ade76fe5b5 100644 --- a/testprogs/threadsignaltest.c +++ b/testprogs/threadsignaltest.c @@ -157,7 +157,7 @@ capture_thread_func(THREAD_FUNC_ARG_TYPE arg) } else printf("No packets seen by pcap_dispatch\n"); } - if (status == -2) { + if (status == PCAP_ERROR_BREAK) { /* * We got interrupted, so perhaps we didn't * manage to finish a line we were printing. @@ -167,11 +167,11 @@ capture_thread_func(THREAD_FUNC_ARG_TYPE arg) printf("Loop got broken\n"); } (void)fflush(stdout); - if (status == -1) { + if (status == PCAP_ERROR) { /* * Error. Report it. */ - (void)fprintf(stderr, "%s: pcap_loop: %s\n", + (void)fprintf(stderr, "%s: pcap_dispatch: %s\n", program_name, pcap_geterr(pd)); } return 0; @@ -182,7 +182,7 @@ main(int argc, char **argv) { register int op; register char *cp, *cmdbuf, *device; - int immediate = 0; + int do_wakeup = 1; pcap_if_t *devlist; bpf_u_int32 localnet, netmask; struct bpf_program fcode; @@ -200,13 +200,17 @@ main(int argc, char **argv) program_name = argv[0]; opterr = 0; - while ((op = getopt(argc, argv, "i:")) != -1) { + while ((op = getopt(argc, argv, "i:n")) != -1) { switch (op) { case 'i': device = optarg; break; + case 'n': + do_wakeup = 0; + break; + default: usage(); /* NOTREACHED */ @@ -229,12 +233,6 @@ main(int argc, char **argv) if (status != 0) error("%s: pcap_set_snaplen failed: %s", device, pcap_statustostr(status)); - if (immediate) { - status = pcap_set_immediate_mode(pd, 1); - if (status != 0) - error("%s: pcap_set_immediate_mode failed: %s", - device, pcap_statustostr(status)); - } status = pcap_set_timeout(pd, 5*60*1000); if (status != 0) error("%s: pcap_set_timeout failed: %s", @@ -280,21 +278,39 @@ main(int argc, char **argv) error("Can't create capture thread: %s", strerror(status)); #endif sleep_secs(60); + printf("Doing pcap_breakloop()\n"); pcap_breakloop(pd); + if (do_wakeup) { + /* + * Force a wakeup in the capture thread. + * + * On some platforms, with some devices,, pcap_breakloop() + * can't do that itself. On Windows, poke the device's + * event handle; on UN*X, send a SIGUSR1 to the thread. + */ +#ifdef _WIN32 + printf("Setting event\n"); + if (!SetEvent(pcap_getevent(pd))) + error("Can't set event for pcap_t: %s", + win32_strerror(GetLastError())); +#else + printf("Sending SIGUSR1\n"); + status = pthread_kill(capture_thread, SIGUSR1); + if (status != 0) + warning("Can't interrupt capture thread: %s", + strerror(status)); +#endif + } + + /* + * Now wait for the capture thread to terminate. + */ #ifdef _WIN32 - printf("Setting event\n"); - if (!SetEvent(pcap_getevent(pd))) - error("Can't set event for pcap_t: %s", - win32_strerror(GetLastError())); if (WaitForSingleObject(capture_thread, INFINITE) == WAIT_FAILED) error("Wait for thread termination failed: %s", win32_strerror(GetLastError())); CloseHandle(capture_thread); #else - printf("Sending SIGUSR1\n"); - status = pthread_kill(capture_thread, SIGUSR1); - if (status != 0) - warning("Can't interrupt capture thread: %s", strerror(status)); status = pthread_join(capture_thread, &retval); if (status != 0) error("Wait for thread termination failed: %s", @@ -317,7 +333,7 @@ countme(u_char *user, const struct pcap_pkthdr *h _U_, const u_char *sp _U_) static void usage(void) { - (void)fprintf(stderr, "Usage: %s [ -m ] [ -i interface ] [ -t timeout] [expression]\n", + (void)fprintf(stderr, "Usage: %s [ -n ] [ -i interface ] [ expression ]\n", program_name); exit(1); } @@ -365,7 +381,7 @@ static char * copy_argv(register char **argv) { register char **p; - register u_int len = 0; + register size_t len = 0; char *buf; char *src, *dst; diff --git a/testprogs/valgrindtest.c b/testprogs/valgrindtest.c index 104ef6a9feaa..55055ca3d9ba 100644 --- a/testprogs/valgrindtest.c +++ b/testprogs/valgrindtest.c @@ -59,6 +59,7 @@ The Regents of the University of California. All rights reserved.\n"; #include #include #include +#include #include #include #include @@ -99,6 +100,23 @@ The Regents of the University of California. All rights reserved.\n"; #endif +/* + * Squelch a warning. + * + * We include system headers to be able to directly set the filter to + * a program with uninitialized content, to make sure what we're testing + * is Valgrind's checking of the system call to set the filter, and we + * also include to open the device in the first place, and that + * means that we may get collisions between their definitions of + * BPF_STMT and BPF_JUMP - and do, in fact, get them on Linux (the + * definitions may be semantically the same, but that's not sufficient to + * avoid the warnings, as the preprocessor doesn't know that u_short is + * just unsigned short). + * + * So we undefine BPF_STMT and BPF_JUMP to avoid the warning. + */ +#undef BPF_STMT +#undef BPF_JUMP #include static char *program_name; @@ -132,11 +150,21 @@ read_infile(char *fname) if (fstat(fd, &buf) < 0) error("can't stat %s: %s", fname, pcap_strerror(errno)); + /* + * _read(), on Windows, has an unsigned int byte count and an + * int return value, so we can't handle a file bigger than + * INT_MAX - 1 bytes (and have no reason to do so; a filter *that* + * big will take forever to compile). (The -1 is for the '\0' at + * the end of the string.) + */ + if (buf.st_size > INT_MAX - 1) + error("%s is larger than %d bytes; that's too large", fname, + INT_MAX - 1); cp = malloc((u_int)buf.st_size + 1); if (cp == NULL) error("malloc(%d) for %s: %s", (u_int)buf.st_size + 1, fname, pcap_strerror(errno)); - cc = read(fd, cp, (u_int)buf.st_size); + cc = (int)read(fd, cp, (u_int)buf.st_size); if (cc < 0) error("read %s: %s", fname, pcap_strerror(errno)); if (cc != buf.st_size) @@ -196,7 +224,7 @@ static char * copy_argv(register char **argv) { register char **p; - register u_int len = 0; + register size_t len = 0; char *buf; char *src, *dst; @@ -421,7 +449,7 @@ usage(void) (void)fprintf(stderr, "%s, with %s\n", program_name, pcap_lib_version()); (void)fprintf(stderr, - "Usage: %s [-aI] [ -F file ] [ -I interface ] [ expression ]\n", + "Usage: %s [-aI] [ -F file ] [ -i interface ] [ expression ]\n", program_name); exit(1); } diff --git a/testprogs/visopts.py b/testprogs/visopts.py new file mode 100755 index 000000000000..97eafffff4f0 --- /dev/null +++ b/testprogs/visopts.py @@ -0,0 +1,317 @@ +#!/usr/bin/env python + +""" +This program parses the output from pcap_compile() to visualize the CFG after +each optimize phase. + +Usage guide: +1. Enable optimizer debugging code when configure libpcap, + and build libpcap & the test programs + ./configure --enable-optimizer-dbg + make + make testprogs +2. Run filtertest to compile BPF expression and produce the CFG as a + DOT graph, save to output a.txt + testprogs/filtertest -g EN10MB host 192.168.1.1 > a.txt +3. Send a.txt to this program's standard input + cat a.txt | testprogs/visopts.py + (Graphviz must be installed) +4. Step 2&3 can be merged: + testprogs/filtertest -g EN10MB host 192.168.1.1 | testprogs/visopts.py +5. The standard output is something like this: + generated files under directory: /tmp/visopts-W9ekBw + the directory will be removed when this programs finished. + open this link: http://localhost:39062/expr1.html +6. Open the URL at the 3rd line in a browser. + +Note: +1. The CFG is translated to SVG images, expr1.html embeds them as external + documents. If you open expr1.html as local file using file:// protocol, some + browsers will deny such requests so the web page will not work properly. + For Chrome, you can run it using the following command to avoid this: + chromium --disable-web-security + That's why this program starts a localhost HTTP server. +2. expr1.html uses jQuery from https://ajax.googleapis.com, so it needs Internet + access to work. +""" + +import sys, os +import string +import subprocess +import json + +html_template = string.Template(""" + + + BPF compiler optimization phases for $expr + + + + + + + +

          +

          $expr

          +
          + +          + +
          +
          +
          + +
          +
          +
          +
          +
          + + +""") + +def write_html(expr, gcount, logs): + logs = map(lambda s: s.strip().replace("\n", "
          "), logs) + + global html_template + html = html_template.safe_substitute(expr=expr.encode("string-escape"), gcount=gcount, logs=json.dumps(logs).encode("string-escape")) + with file("expr1.html", "wt") as f: + f.write(html) + +def render_on_html(infile): + expr = None + gid = 1 + log = "" + dot = "" + indot = 0 + logs = [] + + for line in infile: + if line.startswith("machine codes for filter:"): + expr = line[len("machine codes for filter:"):].strip() + break + elif line.startswith("digraph BPF {"): + indot = 1 + dot = line + elif indot: + dot += line + if line.startswith("}"): + indot = 2 + else: + log += line + + if indot == 2: + try: + p = subprocess.Popen(['dot', '-Tsvg'], stdin=subprocess.PIPE, stdout=subprocess.PIPE) + except OSError as ose: + print "Failed to run 'dot':", ose + print "(Is Graphviz installed?)" + exit(1) + + svg = p.communicate(dot)[0] + with file("expr1_g%03d.svg" % (gid), "wt") as f: + f.write(svg) + + logs.append(log) + gid += 1 + log = "" + dot = "" + indot = 0 + + if indot != 0: + #unterminated dot graph for expression + return False + if expr is None: + # BPF parser encounter error(s) + return False + write_html(expr, gid - 1, logs) + return True + +def run_httpd(): + import SimpleHTTPServer + import SocketServer + + class MySocketServer(SocketServer.TCPServer): + allow_reuse_address = True + Handler = SimpleHTTPServer.SimpleHTTPRequestHandler + httpd = MySocketServer(("localhost", 0), Handler) + print "open this link: http://localhost:%d/expr1.html" % (httpd.server_address[1]) + try: + httpd.serve_forever() + except KeyboardInterrupt as e: + pass + +def main(): + import tempfile + import atexit + import shutil + os.chdir(tempfile.mkdtemp(prefix="visopts-")) + atexit.register(shutil.rmtree, os.getcwd()) + print "generated files under directory: %s" % os.getcwd() + print " the directory will be removed when this program has finished." + + if not render_on_html(sys.stdin): + return 1 + run_httpd() + return 0 + +if __name__ == "__main__": + if '-h' in sys.argv or '--help' in sys.argv: + print __doc__ + exit(0) + exit(main()) diff --git a/testprogs/writecaptest.c b/testprogs/writecaptest.c new file mode 100644 index 000000000000..4db532c6e621 --- /dev/null +++ b/testprogs/writecaptest.c @@ -0,0 +1,556 @@ +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "varattrs.h" + +#ifndef lint +static const char copyright[] _U_ = + "@(#) Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000\n\ +The Regents of the University of California. All rights reserved.\n"; +#endif + +#include +#include +#include +#include +#include +#ifdef _WIN32 + #include "getopt.h" +#else + #include +#endif +#include +#ifndef _WIN32 + #include +#endif +#include + +#include + +#include "pcap/funcattrs.h" + +#ifdef _WIN32 + #include "portability.h" +#endif + +static char *program_name; + +/* Forwards */ +static void PCAP_NORETURN usage(void); +static void PCAP_NORETURN error(const char *, ...) PCAP_PRINTFLIKE(1, 2); +static void warning(const char *, ...) PCAP_PRINTFLIKE(1, 2); +static char *copy_argv(char **); + +static pcap_t *pd; + +#ifdef _WIN32 +static BOOL WINAPI +stop_capture(DWORD ctrltype _U_) +{ + pcap_breakloop(pd); + return TRUE; +} +#else +static void +stop_capture(int signum _U_) +{ + pcap_breakloop(pd); +} +#endif + +static long +parse_interface_number(const char *device) +{ + const char *p; + long devnum; + char *end; + + /* + * Search for a colon, terminating any scheme at the beginning + * of the device. + */ + p = strchr(device, ':'); + if (p != NULL) { + /* + * We found it. Is it followed by "//"? + */ + p++; /* skip the : */ + if (strncmp(p, "//", 2) == 0) { + /* + * Yes. Search for the next /, at the end of the + * authority part of the URL. + */ + p += 2; /* skip the // */ + p = strchr(p, '/'); + if (p != NULL) { + /* + * OK, past the / is the path. + */ + device = p + 1; + } + } + } + devnum = strtol(device, &end, 10); + if (device != end && *end == '\0') { + /* + * It's all-numeric, but is it a valid number? + */ + if (devnum <= 0) { + /* + * No, it's not an ordinal. + */ + error("Invalid adapter index"); + } + return (devnum); + } else { + /* + * It's not all-numeric; return -1, so our caller + * knows that. + */ + return (-1); + } +} + +static char * +find_interface_by_number(long devnum) +{ + pcap_if_t *dev, *devlist; + long i; + char ebuf[PCAP_ERRBUF_SIZE]; + char *device; + int status; + + status = pcap_findalldevs(&devlist, ebuf); + if (status < 0) + error("%s", ebuf); + /* + * Look for the devnum-th entry in the list of devices (1-based). + */ + for (i = 0, dev = devlist; i < devnum-1 && dev != NULL; + i++, dev = dev->next) + ; + if (dev == NULL) + error("Invalid adapter index"); + device = strdup(dev->name); + pcap_freealldevs(devlist); + return (device); +} + +static pcap_t * +open_interface(const char *device, int snaplen_set, int snaplen, char *ebuf) +{ + pcap_t *pc; + int status; + char *cp; + + pc = pcap_create(device, ebuf); + if (pc == NULL) { + /* + * If this failed with "No such device", that means + * the interface doesn't exist; return NULL, so that + * the caller can see whether the device name is + * actually an interface index. + */ + if (strstr(ebuf, "No such device") != NULL) + return (NULL); + error("%s", ebuf); + } + if (snaplen_set) { + status = pcap_set_snaplen(pc, snaplen); + if (status != 0) + error("%s: pcap_set_snaplen failed: %s", + device, pcap_statustostr(status)); + } + status = pcap_set_timeout(pc, 100); + if (status != 0) + error("%s: pcap_set_timeout failed: %s", + device, pcap_statustostr(status)); + status = pcap_activate(pc); + if (status < 0) { + /* + * pcap_activate() failed. + */ + cp = pcap_geterr(pc); + if (status == PCAP_ERROR) + error("%s", cp); + else if (status == PCAP_ERROR_NO_SUCH_DEVICE) { + /* + * Return an error for our caller to handle. + */ + snprintf(ebuf, PCAP_ERRBUF_SIZE, "%s: %s\n(%s)", + device, pcap_statustostr(status), cp); + } else if (status == PCAP_ERROR_PERM_DENIED && *cp != '\0') + error("%s: %s\n(%s)", device, + pcap_statustostr(status), cp); + else + error("%s: %s", device, + pcap_statustostr(status)); + pcap_close(pc); + return (NULL); + } else if (status > 0) { + /* + * pcap_activate() succeeded, but it's warning us + * of a problem it had. + */ + cp = pcap_geterr(pc); + if (status == PCAP_WARNING) + warning("%s", cp); + else if (status == PCAP_WARNING_PROMISC_NOTSUP && + *cp != '\0') + warning("%s: %s\n(%s)", device, + pcap_statustostr(status), cp); + else + warning("%s: %s", device, + pcap_statustostr(status)); + } + return (pc); +} + +#define COMMAND_OPTIONS "DLi:s:w:y:" + +int +main(int argc, char **argv) +{ + int op; + char *cp, *cmdbuf = NULL, *device, *end, *savefile = NULL; + int snaplen = 0; + int snaplen_set = 0; + pcap_if_t *devlist; + long devnum; + int show_interfaces = 0; + int show_dlt_types = 0; + int ndlts; + int *dlts; + bpf_u_int32 localnet, netmask; + struct bpf_program fcode; + char ebuf[PCAP_ERRBUF_SIZE]; +#ifndef _WIN32 + struct sigaction action; +#endif + int dlt; + const char *dlt_name = NULL; + int status; + pcap_dumper_t *pdd; + + device = NULL; + if ((cp = strrchr(argv[0], '/')) != NULL) + program_name = cp + 1; + else + program_name = argv[0]; + + opterr = 0; + while ((op = getopt(argc, argv, COMMAND_OPTIONS)) != -1) { + switch (op) { + + case 'D': + show_interfaces = 1; + break; + + case 'L': + show_dlt_types = 1; + break; + + case 'i': + device = optarg; + break; + + case 's': + snaplen = (int)strtol(optarg, &end, 0); + if (optarg == end || *end != '\0' || snaplen < 0) + error("invalid snaplen %s (must be >= 0)", + optarg); + snaplen_set = 1; + break; + + case 'w': + savefile = optarg; + break; + + case 'y': + dlt_name = optarg; + break; + + default: + usage(); + /* NOTREACHED */ + } + } + + if (show_interfaces) { + pcap_if_t *dev; + int i; + + if (pcap_findalldevs(&devlist, ebuf) < 0) + error("%s", ebuf); + for (i = 0, dev = devlist; dev != NULL; i++, dev = dev->next) { + printf("%d.%s", i+1, dev->name); + if (dev->description != NULL) + printf(" (%s)", dev->description); + printf("\n"); + } + pcap_freealldevs(devlist); + return (0); + } + + if (device == NULL) { + if (pcap_findalldevs(&devlist, ebuf) == -1) + error("%s", ebuf); + if (devlist == NULL) + error("no interfaces available for capture"); + device = strdup(devlist->name); + pcap_freealldevs(devlist); + } + if (show_dlt_types) { + pd = pcap_create(device, ebuf); + if (pd == NULL) + error("%s", ebuf); + status = pcap_activate(pd); + if (status < 0) { + /* + * pcap_activate() failed. + */ + error("%s: %s\n(%s)", device, + pcap_statustostr(status), pcap_geterr(pd)); + } + ndlts = pcap_list_datalinks(pd, &dlts); + if (ndlts < 0) { + /* + * pcap_list_datalinks() failed. + */ + error("%s: %s\n(%s)", device, + pcap_statustostr(status), pcap_geterr(pd)); + } + for (int i = 0; i < ndlts; i++) { + dlt_name = pcap_datalink_val_to_name(dlts[i]); + if (dlt_name == NULL) + printf("DLT %d", dlts[i]); + else + printf("%s", dlt_name); + printf("\n"); + } + pcap_free_datalinks(dlts); + pcap_close(pd); + return 0; + } + + if (savefile == NULL) + error("no savefile specified"); + + *ebuf = '\0'; + + pd = open_interface(device, snaplen_set, snaplen, ebuf); + if (pd == NULL) { + /* + * That failed because the interface couldn't be found. + * + * If we can get a list of interfaces, and the interface name + * is purely numeric, try to use it as a 1-based index + * in the list of interfaces. + */ + devnum = parse_interface_number(device); + if (devnum == -1) { + /* + * It's not a number; just report + * the open error and fail. + */ + error("%s", ebuf); + } + + /* + * OK, it's a number; try to find the + * interface with that index, and try + * to open it. + * + * find_interface_by_number() exits if it + * couldn't be found. + */ + device = find_interface_by_number(devnum); + pd = open_interface(device, snaplen_set, snaplen, ebuf); + if (pd == NULL) + error("%s", ebuf); + } + + if (pcap_lookupnet(device, &localnet, &netmask, ebuf) < 0) { + localnet = 0; + netmask = 0; + warning("%s", ebuf); + } + + if (dlt_name != NULL) { + dlt = pcap_datalink_name_to_val(dlt_name); + if (dlt == PCAP_ERROR) + error("%s isn't a valid DLT name", dlt_name); + if (pcap_set_datalink(pd, dlt) == PCAP_ERROR) + error("%s: %s", device, pcap_geterr(pd)); + } + + /* + * Don't set a filter unless we were given one on the + * command line; if capturing doesn't work, or doesn't + * use the snapshot length, without a filter, that's + * a bug. + */ + if (optind < argc) { + cmdbuf = copy_argv(&argv[optind]); + + if (pcap_compile(pd, &fcode, cmdbuf, 1, netmask) < 0) + error("%s", pcap_geterr(pd)); + + if (pcap_setfilter(pd, &fcode) < 0) + error("%s", pcap_geterr(pd)); + } + + pdd = pcap_dump_open(pd, savefile); + if (pdd == NULL) + error("%s", pcap_geterr(pd)); + +#ifdef _WIN32 + SetConsoleCtrlHandler(stop_capture, TRUE); +#else + action.sa_handler = stop_capture; + sigemptyset(&action.sa_mask); + action.sa_flags = 0; + if (sigaction(SIGINT, &action, NULL) == -1) + error("Can't catch SIGINT: %s\n", strerror(errno)); +#endif + + printf("Listening on %s, link-type ", device); + dlt = pcap_datalink(pd); + dlt_name = pcap_datalink_val_to_name(dlt); + if (dlt_name == NULL) + printf("DLT %d", dlt); + else + printf("%s", dlt_name); + printf("\n"); + for (;;) { + status = pcap_dispatch(pd, -1, pcap_dump, (u_char *)pdd); + if (status < 0) + break; + if (status != 0) { + printf("%d packets seen\n", status); + struct pcap_stat ps; + pcap_stats(pd, &ps); + printf("%d ps_recv, %d ps_drop, %d ps_ifdrop\n", + ps.ps_recv, ps.ps_drop, ps.ps_ifdrop); + } + } + if (status == -2) { + /* + * We got interrupted, so perhaps we didn't + * manage to finish a line we were printing. + * Print an extra newline, just in case. + */ + putchar('\n'); + printf("Broken out of loop from SIGINT handler\n"); + } + (void)fflush(stdout); + if (status == -1) { + /* + * Error. Report it. + */ + (void)fprintf(stderr, "%s: pcap_dispatch: %s\n", + program_name, pcap_geterr(pd)); + } + pcap_close(pd); + if (cmdbuf != NULL) { + pcap_freecode(&fcode); + free(cmdbuf); + } + exit(status == -1 ? 1 : 0); +} + +static void +usage(void) +{ + (void)fprintf(stderr, "Usage: %s -D -L [ -i interface ] [ -s snaplen ] [ -w file ] [ -y dlt ] [expression]\n", + program_name); + exit(1); +} + +/* VARARGS */ +static void +error(const char *fmt, ...) +{ + va_list ap; + + (void)fprintf(stderr, "%s: ", program_name); + va_start(ap, fmt); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + if (*fmt) { + fmt += strlen(fmt); + if (fmt[-1] != '\n') + (void)fputc('\n', stderr); + } + exit(1); + /* NOTREACHED */ +} + +/* VARARGS */ +static void +warning(const char *fmt, ...) +{ + va_list ap; + + (void)fprintf(stderr, "%s: WARNING: ", program_name); + va_start(ap, fmt); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + if (*fmt) { + fmt += strlen(fmt); + if (fmt[-1] != '\n') + (void)fputc('\n', stderr); + } +} + +/* + * Copy arg vector into a new buffer, concatenating arguments with spaces. + */ +static char * +copy_argv(register char **argv) +{ + register char **p; + register size_t len = 0; + char *buf; + char *src, *dst; + + p = argv; + if (*p == 0) + return 0; + + while (*p) + len += strlen(*p++) + 1; + + buf = (char *)malloc(len); + if (buf == NULL) + error("copy_argv: malloc"); + + p = argv; + dst = buf; + while ((src = *p++) != NULL) { + while ((*dst++ = *src++) != '\0') + ; + dst[-1] = ' '; + } + dst[-1] = '\0'; + + return buf; +} diff --git a/tests/pcap-invalid-version-1.pcap b/tests/pcap-invalid-version-1.pcap new file mode 100644 index 000000000000..9dd0429d22f7 Binary files /dev/null and b/tests/pcap-invalid-version-1.pcap differ diff --git a/tests/pcap-invalid-version-2.pcap b/tests/pcap-invalid-version-2.pcap new file mode 100644 index 000000000000..4217d1e7a9ec Binary files /dev/null and b/tests/pcap-invalid-version-2.pcap differ diff --git a/tests/pcapng-invalid-vers-1.pcapng b/tests/pcapng-invalid-vers-1.pcapng new file mode 100644 index 000000000000..7bbb7ab0cdae Binary files /dev/null and b/tests/pcapng-invalid-vers-1.pcapng differ diff --git a/tests/pcapng-invalid-vers-2.pcapng b/tests/pcapng-invalid-vers-2.pcapng new file mode 100644 index 000000000000..77595f4bda1a Binary files /dev/null and b/tests/pcapng-invalid-vers-2.pcapng differ