/libpcap/pcap-linux.c
C | 6603 lines | 3429 code | 559 blank | 2615 comment | 832 complexity | 03a345b611892f4e8203020796700711 MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-2.0, Apache-2.0, LGPL-2.0, LGPL-2.1, MIT
Large files files are truncated, but you can click here to view the full file
- /*
- * pcap-linux.c: Packet capture interface to the Linux kernel
- *
- * Copyright (c) 2000 Torsten Landschoff <torsten@debian.org>
- * Sebastian Krahmer <krahmer@cs.uni-potsdam.de>
- *
- * License: BSD
- *
- * 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. The names of the authors may not 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.
- *
- * Modifications: Added PACKET_MMAP support
- * Paolo Abeni <paolo.abeni@email.it>
- * Added TPACKET_V3 support
- * Gabor Tatarka <gabor.tatarka@ericsson.com>
- *
- * based on previous works of:
- * Simon Patarin <patarin@cs.unibo.it>
- * Phil Wood <cpw@lanl.gov>
- *
- * Monitor-mode support for mac80211 includes code taken from the iw
- * command; the copyright notice for that code is
- *
- * Copyright (c) 2007, 2008 Johannes Berg
- * Copyright (c) 2007 Andy Lutomirski
- * Copyright (c) 2007 Mike Kershaw
- * Copyright (c) 2008 Gรกbor Stefanik
- *
- * 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. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
- */
- /*
- * 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
- #ifdef HAVE_CONFIG_H
- #include "config.h"
- #endif
- #include <errno.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <ctype.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <string.h>
- #include <limits.h>
- #include <sys/stat.h>
- #include <sys/socket.h>
- #include <sys/ioctl.h>
- #include <sys/utsname.h>
- #include <sys/mman.h>
- #include <linux/if.h>
- #include <linux/if_packet.h>
- #include <linux/sockios.h>
- #include <netinet/in.h>
- #include <linux/if_ether.h>
- #include <net/if_arp.h>
- #include <poll.h>
- #include <dirent.h>
- #include "pcap-int.h"
- #include "pcap/sll.h"
- #include "pcap/vlan.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 <linux/if_packet.h> rather than
- * <netpacket/packet.h>; 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 <netpacket/packet.h>
- * file;
- *
- * not all versions of glibc2 have a <netpacket/packet.h> 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 <linux/XXX.h> 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 <netpacket/packet.h>
- *
- * XXX - should we just include <linux/if_packet.h> even if PF_PACKET
- * isn't defined? It only defines one data structure in 2.0.x, so
- * it shouldn't cause any problems.
- */
- #ifdef PF_PACKET
- # include <linux/if_packet.h>
- /*
- * On at least some Linux distributions (for example, Red Hat 5.2),
- * there's no <netpacket/packet.h> file, but PF_PACKET is defined if
- * you include <sys/socket.h>, but <linux/if_packet.h> 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 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 */
- #ifdef SO_ATTACH_FILTER
- #include <linux/types.h>
- #include <linux/filter.h>
- #endif
- #ifdef HAVE_LINUX_NET_TSTAMP_H
- #include <linux/net_tstamp.h>
- #endif
- #ifdef HAVE_LINUX_SOCKIOS_H
- #include <linux/sockios.h>
- #endif
- #ifdef HAVE_LINUX_IF_BONDING_H
- #include <linux/if_bonding.h>
- #endif
- /*
- * Got Wireless Extensions?
- */
- #ifdef HAVE_LINUX_WIRELESS_H
- #include <linux/wireless.h>
- #endif /* HAVE_LINUX_WIRELESS_H */
- /*
- * Got libnl?
- */
- #ifdef HAVE_LIBNL
- #include <linux/nl80211.h>
- #include <netlink/genl/genl.h>
- #include <netlink/genl/family.h>
- #include <netlink/genl/ctrl.h>
- #include <netlink/msg.h>
- #include <netlink/attr.h>
- #endif /* HAVE_LIBNL */
- /*
- * Got ethtool support?
- */
- #ifdef HAVE_LINUX_ETHTOOL_H
- #include <linux/ethtool.h>
- #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
- /*
- * When capturing on all interfaces we use this as the buffer size.
- * Should be bigger then all MTUs that occur in real life.
- * 64kB should be enough for now.
- */
- #define BIGGER_THAN_ALL_MTUS (64*1024)
- /*
- * Private data for capturing on Linux SOCK_PACKET or 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 */
- struct pcap_stat stat;
- char *device; /* device name */
- int filter_in_userland; /* must filter in userland */
- 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 */
- 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 */
- size_t mmapbuflen; /* size of region */
- int vlan_offset; /* offset at which to insert vlan tags; if -1, don't insert */
- u_int tp_version; /* version of tpacket_hdr for mmaped ring */
- u_int tp_hdrlen; /* hdrlen of tpacket_hdr for mmaped ring */
- u_char *oneshot_buffer; /* buffer for copy of packet */
- #ifdef HAVE_TPACKET3
- 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
- };
- /*
- * 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 */
- /*
- * Prototypes for internal functions and methods.
- */
- static void map_arphrd_to_dlt(pcap_t *, int, int, const char *, int);
- #ifdef HAVE_PF_PACKET_SOCKETS
- static short int map_packet_type_to_sll_type(short int);
- #endif
- static int pcap_activate_linux(pcap_t *);
- static int activate_old(pcap_t *);
- static int activate_new(pcap_t *);
- static int activate_mmap(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_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;
- };
- #ifdef HAVE_PACKET_RING
- #define RING_GET_FRAME(h) (((union thdr **)h->buffer)[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, char *errbuf);
- static int pcap_getnonblock_mmap(pcap_t *p, char *errbuf);
- static void pcap_oneshot_mmap(u_char *user, const struct pcap_pkthdr *h,
- const u_char *bytes);
- #endif
- #ifdef TP_STATUS_VLAN_TPID_VALID
- # define VLAN_TPID(hdr, hv) (((hv)->tp_vlan_tpid || ((hdr)->tp_status & TP_STATUS_VLAN_TPID_VALID)) ? (hv)->tp_vlan_tpid : ETH_P_8021Q)
- #else
- # define VLAN_TPID(hdr, hv) ETH_P_8021Q
- #endif
- /*
- * 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);
- #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(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_offset(struct bpf_insn *p);
- static int set_kernel_filter(pcap_t *handle, struct sock_fprog *fcode);
- static int reset_kernel_filter(pcap_t *handle);
- 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 */
- pcap_t *
- pcap_create_interface(const char *device, char *ebuf)
- {
- pcap_t *handle;
- handle = pcap_create_common(device, ebuf, sizeof (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(handle, ebuf) == -1) {
- free(handle);
- return NULL;
- }
- #endif
- #if defined(SIOCGSTAMPNS) && defined(SO_TIMESTAMPNS)
- /*
- * We claim that we support microsecond and nanosecond time
- * stamps.
- *
- * XXX - with adapter-supplied time stamps, can we choose
- * 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) {
- snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
- pcap_strerror(errno));
- if (handle->tstamp_type_list != NULL)
- free(handle->tstamp_type_list);
- free(handle);
- return NULL;
- }
- handle->tstamp_precision_list[0] = PCAP_TSTAMP_PRECISION_MICRO;
- handle->tstamp_precision_list[1] = PCAP_TSTAMP_PRECISION_NANO;
- #endif /* defined(SIOCGSTAMPNS) && defined(SO_TIMESTAMPNS) */
- 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}.
- *
- * 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.
- *
- * Yes, you can have multiple monitor devices for a given
- * physical device.
- */
- /*
- * Is this a mac80211 device? If so, fill in the physical device path and
- * return 1; if not, return 0. On an error, fill in handle->errbuf and
- * return PCAP_ERROR.
- */
- static int
- get_mac80211_phydev(pcap_t *handle, const char *device, char *phydev_path,
- size_t phydev_max_pathlen)
- {
- char *pathstr;
- ssize_t bytes_read;
- /*
- * Generate the path string for the symlink to the physical device.
- */
- if (asprintf(&pathstr, "/sys/class/net/%s/phy80211", device) == -1) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "%s: Can't generate path name string for /sys/class/net device",
- device);
- return PCAP_ERROR;
- }
- bytes_read = readlink(pathstr, phydev_path, phydev_max_pathlen);
- if (bytes_read == -1) {
- if (errno == ENOENT || errno == EINVAL) {
- /*
- * Doesn't exist, or not a symlink; assume that
- * means it's not a mac80211 device.
- */
- free(pathstr);
- return 0;
- }
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "%s: Can't readlink %s: %s", device, pathstr,
- strerror(errno));
- free(pathstr);
- return PCAP_ERROR;
- }
- free(pathstr);
- phydev_path[bytes_read] = '\0';
- 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;
- struct genl_family *nl80211;
- };
- static int
- nl80211_init(pcap_t *handle, struct nl80211_state *state, const char *device)
- {
- int err;
- state->nl_sock = nl_socket_alloc();
- if (!state->nl_sock) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "%s: failed to allocate netlink handle", device);
- return PCAP_ERROR;
- }
- if (genl_connect(state->nl_sock)) {
- 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) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "%s: failed to allocate generic netlink cache: %s",
- device, get_nl_errmsg(-err));
- goto out_handle_destroy;
- }
- state->nl80211 = genl_ctrl_search_by_name(state->nl_cache, "nl80211");
- if (!state->nl80211) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "%s: nl80211 not found", device);
- goto out_cache_free;
- }
- return 0;
- out_cache_free:
- nl_cache_free(state->nl_cache);
- out_handle_destroy:
- nl_socket_free(state->nl_sock);
- return PCAP_ERROR;
- }
- static void
- nl80211_cleanup(struct nl80211_state *state)
- {
- genl_family_put(state->nl80211);
- nl_cache_free(state->nl_cache);
- nl_socket_free(state->nl_sock);
- }
- static int
- add_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state,
- const char *device, const char *mondevice)
- {
- int ifindex;
- struct nl_msg *msg;
- int err;
- ifindex = iface_get_id(sock_fd, device, handle->errbuf);
- if (ifindex == -1)
- return PCAP_ERROR;
- msg = nlmsg_alloc();
- if (!msg) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "%s: failed to allocate netlink msg", device);
- return PCAP_ERROR;
- }
- 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);
- NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, mondevice);
- 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
- * NLE_FAILURE; it can also map other errors
- * to that, but there's not much we can do
- * about that.)
- */
- nlmsg_free(msg);
- return 0;
- } else {
- /*
- * Real failure, not just "that device is not
- * available.
- */
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "%s: nl_send_auto_complete failed adding %s interface: %s",
- device, mondevice, get_nl_errmsg(-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
- * NLE_FAILURE; it can also map other errors
- * to that, but there's not much we can do
- * about that.)
- */
- nlmsg_free(msg);
- return 0;
- } else {
- /*
- * Real failure, not just "that device is not
- * available.
- */
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "%s: nl_wait_for_ack failed adding %s interface: %s",
- device, mondevice, get_nl_errmsg(-err));
- nlmsg_free(msg);
- return PCAP_ERROR;
- }
- }
- /*
- * Success.
- */
- nlmsg_free(msg);
- return 1;
- nla_put_failure:
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "%s: nl_put failed adding %s interface",
- device, mondevice);
- nlmsg_free(msg);
- return PCAP_ERROR;
- }
- static int
- del_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state,
- const char *device, const char *mondevice)
- {
- int ifindex;
- struct nl_msg *msg;
- int err;
- ifindex = iface_get_id(sock_fd, mondevice, handle->errbuf);
- if (ifindex == -1)
- return PCAP_ERROR;
- msg = nlmsg_alloc();
- if (!msg) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "%s: failed to allocate netlink msg", device);
- return PCAP_ERROR;
- }
- genlmsg_put(msg, 0, 0, genl_family_get_id(state->nl80211), 0,
- 0, NL80211_CMD_DEL_INTERFACE, 0);
- NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
- err = nl_send_auto_complete(state->nl_sock, msg);
- if (err < 0) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "%s: nl_send_auto_complete failed deleting %s interface: %s",
- device, mondevice, get_nl_errmsg(-err));
- nlmsg_free(msg);
- return PCAP_ERROR;
- }
- err = nl_wait_for_ack(state->nl_sock);
- if (err < 0) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "%s: nl_wait_for_ack failed adding %s interface: %s",
- device, mondevice, get_nl_errmsg(-err));
- nlmsg_free(msg);
- return PCAP_ERROR;
- }
- /*
- * Success.
- */
- nlmsg_free(msg);
- return 1;
- nla_put_failure:
- 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 */
- snprintf(mondevice, sizeof mondevice, "mon%u", n);
- ret = add_mon_if(handle, sock_fd, &nlstate, device, mondevice);
- if (ret == 1) {
- handlep->mondevice = strdup(mondevice);
- goto added;
- }
- if (ret < 0) {
- /*
- * Hard failure. Just return ret; handle->errbuf
- * has already been set.
- */
- nl80211_cleanup(&nlstate);
- return ret;
- }
- }
- 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.
- */
- return PCAP_ERROR_RFMON_NOTSUP;
- }
- /*
- * Now configure the monitor interface up.
- */
- memset(&ifr, 0, sizeof(ifr));
- strlcpy(ifr.ifr_name, handlep->mondevice, sizeof(ifr.ifr_name));
- if (ioctl(sock_fd, SIOCGIFFLAGS, &ifr) == -1) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "%s: Can't get flags for %s: %s", device,
- handlep->mondevice, strerror(errno));
- 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) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "%s: Can't set flags for %s: %s", device,
- handlep->mondevice, strerror(errno));
- 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)
- {
- #if defined(BOND_INFO_QUERY_OLD) || defined(SIOCBONDINFOQUERY)
- struct ifreq ifr;
- ifbond ifb;
- memset(&ifr, 0, sizeof ifr);
- strlcpy(ifr.ifr_name, device, sizeof ifr.ifr_name);
- memset(&ifb, 0, sizeof ifb);
- ifr.ifr_data = (caddr_t)&ifb;
- #ifdef SIOCBONDINFOQUERY
- if (ioctl(fd, SIOCBONDINFOQUERY, &ifr) == 0)
- #else /* SIOCBONDINFOQUERY */
- if (ioctl(fd, BOND_INFO_QUERY_OLD, &ifr) == 0)
- #endif /* SIOCBONDINFOQUERY */
- return 1; /* success, so it's a bonding device */
- #endif /* defined(BOND_INFO_QUERY_OLD) || defined(SIOCBONDINFOQUERY) */
- return 0; /* no, it's not a bonding device */
- }
- #endif /* IW_MODE_MONITOR */
- static int
- pcap_can_set_rfmon_linux(pcap_t *handle)
- {
- #ifdef HAVE_LIBNL
- char phydev_path[PATH_MAX+1];
- int ret;
- #endif
- #ifdef IW_MODE_MONITOR
- int sock_fd;
- struct iwreq ireq;
- #endif
- if (strcmp(handle->opt.source, "any") == 0) {
- /*
- * Monitor mode makes no sense on the "any" device.
- */
- return 0;
- }
- #ifdef HAVE_LIBNL
- /*
- * Bleah. There doesn't seem to be a way to ask a mac80211
- * device, through libnl, whether it supports monitor mode;
- * 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.source, phydev_path,
- PATH_MAX);
- if (ret < 0)
- return ret; /* error */
- if (ret == 1)
- 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, htons(ETH_P_ALL));
- if (sock_fd == -1) {
- (void)snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "socket: %s", pcap_strerror(errno));
- return PCAP_ERROR;
- }
- if (is_bonding_device(sock_fd, handle->opt.source)) {
- /* It's a bonding device, so don't even try. */
- close(sock_fd);
- return 0;
- }
- /*
- * Attempt to get the current mode.
- */
- strlcpy(ireq.ifr_ifrn.ifrn_name, handle->opt.source,
- 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. */
- (void)snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "SIOCGIWMODE failed: %s", pcap_strerror(errno));
- 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.
- *
- * 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?
- */
- static long int
- linux_if_drops(const char * if_name)
- {
- char buffer[512];
- char * bufptr;
- FILE * file;
- int field_to_convert = 3, if_name_sz = strlen(if_name);
- long int dropped_pkts = 0;
- file = fopen("/proc/net/dev", "r");
- if (!file)
- return 0;
- while (!dropped_pkts && fgets( buffer, sizeof(buffer), file ))
- {
- /* 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;
- }
- /* find iface and make sure it actually matches -- space before the name and : after it */
- if ((bufptr = strstr(buffer, if_name)) &&
- (bufptr == buffer || *(bufptr-1) == ' ') &&
- *(bufptr + if_name_sz) == ':')
- {
- bufptr = bufptr + if_name_sz + 1;
- /* grab the nth field from it */
- while( --field_to_convert && *bufptr != '\0')
- {
- while (*bufptr != '\0' && *(bufptr++) == ' ');
- while (*bufptr != '\0' && *(bufptr++) != ' ');
- }
- /* get rid of any final spaces */
- while (*bufptr != '\0' && *bufptr == ' ') bufptr++;
- if (*bufptr != '\0')
- dropped_pkts = strtol(bufptr, NULL, 10);
- break;
- }
- }
- fclose(file);
- return dropped_pkts;
- }
- /*
- * 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.
- */
- 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));
- 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);
- if (ret >= 0) {
- ret = del_mon_if(handle, handle->fd, &nlstate,
- handlep->device, handlep->mondevice);
- nl80211_cleanup(&nlstate);
- }
- if (ret < 0) {
- fprintf(stderr,
- "Can't delete monitor interface %s (%s).\n"
- "Please delete manually.\n",
- handlep->mondevice, handle->errbuf);
- }
- }
- #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));
- 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.
- */
- 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.
- */
- pcap_remove_from_pcaps_to_close(handle);
- }
- if (handlep->mondevice != NULL) {
- free(handlep->mondevice);
- handlep->mondevice = NULL;
- }
- if (handlep->device != NULL) {
- free(handlep->device);
- handlep->device = NULL;
- }
- pcap_cleanup_live_common(handle);
- }
- /*
- * 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
- * be deprecated and functions be added to select that later allow
- * modification of that values -- Torsten).
- */
- static int
- pcap_activate_linux(pcap_t *handle)
- {
- struct pcap_linux *handlep = handle->priv;
- const char *device;
- struct ifreq ifr;
- int status = 0;
- int ret;
- device = handle->opt.source;
- /*
- * Make sure the name we were handed will fit into the ioctls we
- * might perform on the device; if not, return a "No such device"
- * indication, as the Linux kernel shouldn't support creating
- * a device whose name won't fit into those ioctls.
- *
- * "Will fit" means "will fit, complete with a null terminator",
- * so if the length, which does *not* include the null terminator,
- * is greater than *or equal to* the size of the field into which
- * we'll be copying it, that won't fit.
- */
- if (strlen(device) >= sizeof(ifr.ifr_name)) {
- status = PCAP_ERROR_NO_SUCH_DEVICE;
- 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->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. */
- 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) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "strdup: %s",
- pcap_strerror(errno) );
- return PCAP_ERROR;
- }
- /* 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
- */
- if (handle->opt.promisc)
- handlep->proc_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.
- */
- ret = activate_new(handle);
- 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.
- */
- 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.
- */
- 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.
- * ret has been set to the error status to
- * return and, if it's PCAP_ERROR, handle->errbuf
- * contains the error message.
- */
- status = ret;
- goto fail;
- }
- }
- 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.
- */
- if (handle->opt.buffer_size != 0) {
- /*
- * Set the socket buffer size to the specified value.
- */
- if (setsockopt(handle->fd, SOL_SOCKET, SO_RCVBUF,
- &handle->opt.buffer_size,
- sizeof(handle->opt.buffer_size)) == -1) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "SO_RCVBUF: %s", pcap_strerror(errno));
- status = PCAP_ERROR;
- goto fail;
- }
- }
- /* Allocate the buffer */
- handle->buffer = malloc(handle->bufsize + handle->offset);
- if (!handle->buffer) {
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "malloc: %s", pcap_strerror(errno));
- status = PCAP_ERROR;
- goto fail;
- }
- /*
- * "handle->fd" is a socket, so "select()" and "poll()"
- * should work on it.
- */
- handle->selectable_fd = handle->fd;
- return status;
- fail:
- pcap_cleanup_linux(handle);
- 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, 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;
- return 0;
- }
- /*
- * linux_check_direction()
- *
- * Do checks based on packet direction.
- */
- static inline int
- linux_check_direction(const pcap_t *handle, const struct sockaddr_ll *sll)
- {
- struct pcap_linux *handlep = handle->priv;
- if (sll->sll_pkttype == PACKET_OUTGOING) {
- /*
- * Outgoing packet.
- * If this is from the loopback device, reject it;
- * we'll see the packet as an incoming packet as well,
- * and we don't want to see it twice.
- */
- if (sll->sll_ifindex == handlep->lo_ifindex)
- return 0;
- /*
- * If the user only wants incoming packets, reject it.
- */
- if (handle->direction == PCAP_D_IN)
- return 0;
- } else {
- /*
- * Incoming packet.
- * If the user only wants outgoing packets, reject it.
- */
- if (handle->direction == PCAP_D_OUT)
- return 0;
- }
- return 1;
- }
- /*
- * 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.
- */
- static int
- pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata)
- {
- struct pcap_linux *handlep = handle->priv;
- u_char *bp;
- int offset;
- #ifdef HAVE_PF_PACKET_SOCKETS
- struct sockaddr_ll from;
- struct sll_header *hdrp;
- #else
- struct sockaddr from;
- #endif
- #if defined(HAVE_PACKET_AUXDATA) && defined(HAVE_LINUX_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_LINUX_TPACKET_AUXDATA_TP_VLAN_TCI) */
- socklen_t fromlen;
- #endif /* defined(HAVE_PACKET_AUXDATA) && defined(HAVE_LINUX_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)
- 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
- /*
- * 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.
- */
- bp = handle->buffer + handle->offset;
- #if defined(HAVE_PACKET_AUXDATA) && defined(HAVE_LINUX_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_LINUX_TPACKET_AUXDATA_TP_VLAN_TCI) */
- do {
- /*
- * Has "pcap_breakloop()" been called?
- */
- 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_LINUX_TPACKET_AUXDATA_TP_VLAN_TCI)
- packet_len = recvmsg(handle->fd, &msg, MSG_TRUNC);
- #else /* defined(HAVE_PACKET_AUXDATA) && defined(HAVE_LINUX_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_LINUX_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.
- */
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "The interface went down");
- return PCAP_ERROR;
- default:
- snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
- "recvfrom: %s", pcap_strerror(errno));
- return PCAP_ERROR;
- }
- }
- #ifdef HAVE_PF_PACKET_SOCKETS
- if (!handlep->sock_packet) {
- /*
- * 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.)
- */
- 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 …
Large files files are truncated, but you can click here to view the full file