From 7d95f956e203b13be1bf91090531449359692724 Mon Sep 17 00:00:00 2001 From: Emmanuel Roullit Date: Sat, 31 Dec 2011 16:21:22 +0100 Subject: [PATCH] Added functions to make PCAP from received packets. --- dabbacore/CMakeLists.txt | 2 +- dabbacore/include/dabbacore/pcap.h | 109 ++++++++++++++ dabbacore/packet_rx.c | 17 ++- dabbacore/pcap.c | 281 +++++++++++++++++++++++++++++++++++++ 4 files changed, 401 insertions(+), 8 deletions(-) create mode 100644 dabbacore/include/dabbacore/pcap.h create mode 100644 dabbacore/pcap.c diff --git a/dabbacore/CMakeLists.txt b/dabbacore/CMakeLists.txt index 94a65c8..1baa673 100644 --- a/dabbacore/CMakeLists.txt +++ b/dabbacore/CMakeLists.txt @@ -11,7 +11,7 @@ IF(DOXYGEN_FOUND) ADD_CUSTOM_TARGET(doc ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) ENDIF(DOXYGEN_FOUND) -ADD_LIBRARY(${PROJECT_NAME} SHARED packet_mmap.c nic.c strlcpy.c packet_rx.c) +ADD_LIBRARY(${PROJECT_NAME} SHARED packet_mmap.c nic.c strlcpy.c pcap.c packet_rx.c) TARGET_LINK_LIBRARIES(${PROJECT_NAME}) diff --git a/dabbacore/include/dabbacore/pcap.h b/dabbacore/include/dabbacore/pcap.h new file mode 100644 index 0000000..ca61a8c --- /dev/null +++ b/dabbacore/include/dabbacore/pcap.h @@ -0,0 +1,109 @@ +/** + * \file packet_rx.c + * \author written by Emmanuel Roullit emmanuel.roullit@gmail.com (c) 2009-2011 + * \date 2011 + */ + +/* __LICENSE_HEADER_BEGIN__ */ + +/* + * Copyright (C) 2009-2011 Emmanuel Roullit + * + * This program 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 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + * + */ + + /* __LICENSE_HEADER_END__ */ + +#ifndef PCAP_H +#define PCAP_H + +#include +#include +#include + +#define TCPDUMP_MAGIC 0xa1b2c3d4 +#define PCAP_VERSION_MAJOR 2 +#define PCAP_VERSION_MINOR 4 +#define PCAP_DEFAULT_SNAPSHOT_LEN 65535 + +/** \brief Enum regrouping all possible PCAP link types */ +enum pcap_linktype { + LINKTYPE_NULL = 0, /**< BSD loopback encapsulation */ + LINKTYPE_EN10MB = 1, /**< Ethernet (10Mb) */ +}; + +/** \brief Structure describing a PCAP file header */ +struct pcap_file_header { + uint32_t magic; /**< if swapped, all fields must be swapped */ + uint16_t version_major; /**< PCAP file major version */ + uint16_t version_minor; /**< PCAP file minor version */ + int32_t thiszone; /**< GMT to local correction leave it zero */ + uint32_t sigfigs; /**< accuracy of timestamps. Set on 0 */ + uint32_t snaplen; /**< max length saved portion of each pkt. */ + uint32_t linktype; /**< data link type (LINKTYPE_*) */ +}; + +/** + * \brief PCAP specifix timestamp + * + * This is a timeval as stored in a savefile. + * It has to use the same types everywhere, independent of the actual + * `struct timeval'; `struct timeval' has 32-bit tv_sec values on some + * platforms and 64-bit tv_sec values on other platforms, and writing + * out native `struct timeval' values would mean files could only be + * read on systems with the same tv_sec size as the system on which + * the file was written. + */ + +struct pcap_timeval { + int32_t tv_sec; /**< seconds */ + int32_t tv_usec; /**< microseconds */ +}; + +/** + * \brief Structure describing per-packet information + * + * The time stamp can and should be a "struct timeval", regardless of + * whether your system supports 32-bit tv_sec in "struct timeval", + * 64-bit tv_sec in "struct timeval", or both if it supports both 32-bit + * and 64-bit applications. The on-disk format of savefiles uses 32-bit + * tv_sec (and tv_usec); this structure is irrelevant to that. 32-bit + * and 64-bit versions of libpcap, even if they're on the same platform, + * should supply the appropriate version of "struct timeval", even if + * that's not what the underlying packet capture mechanism supplies. + */ + +struct pcap_sf_pkthdr { + struct pcap_timeval ts; /**< timestamp */ + uint32_t caplen; /**< length of portion present */ + int32_t len; /**< length this packet (off wire) */ +}; + +static inline int is_linktype_valid(uint32_t linktype) +{ + return (linktype == LINKTYPE_EN10MB); +} + +int pcap_link_type_get(int arp_type, enum pcap_linktype *pcap_link_type); +ssize_t pcap_write(const int fd, const uint8_t * const pkt, + const size_t pkt_len, const size_t pkt_snaplen, + const uint64_t tv_sec, const uint64_t tv_usec); +void pcap_destroy(const int fd, const char *const pcap_path); +int pcap_create(const char *const pcap_path, const enum pcap_linktype linktype); +int pcap_open(const char *const pcap_path, int flags); +int pcap_close(const int fd); + +#endif /* PCAP_H */ diff --git a/dabbacore/packet_rx.c b/dabbacore/packet_rx.c index c39ee92..3a72978 100644 --- a/dabbacore/packet_rx.c +++ b/dabbacore/packet_rx.c @@ -81,13 +81,16 @@ int packet_rx(const struct packet_mmap *pkt_rx, const int pcap_fd) if ((mmap_hdr->tp_h.tp_status & TP_STATUS_USER) == TP_STATUS_USER) { - pcap_write(pcap_fd, - (uint8_t *) mmap_hdr + - mmap_hdr->tp_h.tp_mac, - mmap_hdr->tp_h.tp_len, - mmap_hdr->tp_h.tp_snaplen, - mmap_hdr->tp_h.tp_sec, - mmap_hdr->tp_h.tp_usec); + if (pcap_fd > 0) { + pcap_write(pcap_fd, + (uint8_t *) mmap_hdr + + mmap_hdr->tp_h.tp_mac, + mmap_hdr->tp_h.tp_len, + mmap_hdr->tp_h.tp_snaplen, + mmap_hdr->tp_h.tp_sec, + mmap_hdr->tp_h.tp_usec); + } + mmap_hdr->tp_h.tp_status = TP_STATUS_KERNEL; } } diff --git a/dabbacore/pcap.c b/dabbacore/pcap.c new file mode 100644 index 0000000..3bc7533 --- /dev/null +++ b/dabbacore/pcap.c @@ -0,0 +1,281 @@ +/** + * \file pcap.c + * \author written by Emmanuel Roullit emmanuel.roullit@gmail.com (c) 2009-2011 + * \date 2011 + */ + +/* __LICENSE_HEADER_BEGIN__ */ + +/* + * Copyright (C) 2009-2011 Emmanuel Roullit + * + * This program 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 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + * + */ + + /* __LICENSE_HEADER_END__ */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +/** + * \internal + * \brief Write the PCAP file header on a file descriptor + * \param[in] fd PCAP file descriptor + * \param[in] linktype PCAP link type + * \param[in] thiszone Timezone where the PCAP is created + * \param[in] snaplen Maximum length of a captured packet + * \return 0 on success, -1 if PCAP file header could not be written + */ + +static int pcap_file_header_write(const int fd, const int linktype, + const int thiszone, const int snaplen) +{ + struct pcap_file_header hdr; + + assert(fd > 0); + + memset(&hdr, 0, sizeof(hdr)); + + hdr.magic = TCPDUMP_MAGIC; + hdr.version_major = PCAP_VERSION_MAJOR; + hdr.version_minor = PCAP_VERSION_MINOR; + hdr.thiszone = thiszone; + hdr.snaplen = snaplen; + hdr.sigfigs = 0; + hdr.linktype = linktype; + + if (write(fd, (char *)&hdr, sizeof(hdr)) != sizeof(hdr)) { + return (-1); + } + + return (0); +} + +/** + * \brief Get PCAP link type from NIC ARP type + * \param[in] arp_type ARP type value + * \param[out] pcap_link_type Pointer to the PCAP link type + * \return 0 on success, \c EINVAL when the ARP type is not supported + */ + +int pcap_link_type_get(int arp_type, enum pcap_linktype *pcap_link_type) +{ + int rc = 0; + + assert(pcap_link_type); + + switch (arp_type) { + case ARPHRD_ETHER: + case ARPHRD_LOOPBACK: + *pcap_link_type = LINKTYPE_EN10MB; + break; + default: + rc = EINVAL; + break; + } + + return (rc); +} + +/** + * \brief Validate PCAP file header + * Every PCAP file has a file header which contains: + * - the PCAP magic (\c 0xa1b2c3d4) + * - the PCAP version major/minor + * - the PCAP linktype + * - the timezone + * - the maximum packet length + * \param[in] fd PCAP file descriptor + * \return 0 if the PCAP file header is not valid \n + * 1 if it is. \n + * If PCAP file header is invalid, errno is set to \n + * \c EINVAL if PCAP file descriptor or file header is invalid \n + * \c EIO if PCAP file header could not be read + */ + +int pcap_is_valid(const int fd) +{ + struct pcap_file_header hdr; + + if (fd < 0) { + errno = EINVAL; + return (0); + } + + if (read(fd, (char *)&hdr, sizeof(hdr)) != sizeof(hdr)) { + errno = EIO; + return (0); + } + + if (hdr.magic != TCPDUMP_MAGIC + || hdr.version_major != PCAP_VERSION_MAJOR + || hdr.version_minor != PCAP_VERSION_MINOR + || !is_linktype_valid(hdr.linktype)) { + errno = EINVAL; + return (0); + } + + return (1); +} + +/** + * \brief Create a PCAP file + * \param[in] pcap_path PCAP file path + * \param[in] linktype PCAP link type + * \return PCAP file descriptor on success, -1 on failure + * \note It creates a PCAP file with default permissions + * \note A created PCAP will have by default a snapshot length of 65535 bytes. + */ + +int pcap_create(const char *const pcap_path, const enum pcap_linktype linktype) +{ + assert(pcap_path); + + int fd; + + if ((fd = creat(pcap_path, DEFFILEMODE)) < 0) { + return (-1); + } + + /* TODO make it configurable instead of using default values */ + if (pcap_file_header_write(fd, linktype, 0, PCAP_DEFAULT_SNAPSHOT_LEN)) { + /* When the PCAP header cannot be written the file + * must be closed and then deleted + */ + pcap_destroy(fd, pcap_path); + fd = -1; + } + + return (fd); +} + +/** + * \brief Destroy a PCAP file + * \param[in] fd PCAP file descriptor + * \param[in] pcap_path PCAP file path + */ + +void pcap_destroy(const int fd, const char *const pcap_path) +{ + assert(pcap_path); + assert(fd > 0); + + close(fd); + unlink(pcap_path); +} + +/** + * \brief Open a PCAP file + * \param[in] pcap_path PCAP file path + * \param[in] flags flags for \c open(2) + * \return PCAP file descriptor on success, -1 on failure + * \note The flags given as parameter are directly given to \c open(2) + */ + +int pcap_open(const char *const pcap_path, int flags) +{ + int append = 0; + int fd; + + assert(pcap_path); + + /* Deactivate append to be able to check pcap validity */ + if ((flags & O_APPEND) == O_APPEND) { + append = 1; + flags &= ~O_APPEND; + } + + if ((fd = open(pcap_path, flags)) < 0) { + return (-1); + } + + if (pcap_is_valid(fd) == 0) { + pcap_close(fd); + return (-1); + } + + if (append) { + /* Go to EOF */ + if (lseek(fd, 0, SEEK_END) < 0) { + pcap_close(fd); + return (-1); + } + } + + return (fd); +} + +/** + * \brief Close a PCAP file + * \param[in] fd PCAP file descriptor + * \return same error values as \c close(2) + */ + +int pcap_close(const int fd) +{ + return (close(fd)); +} + +/** + * \brief Write the packet payload on a file descriptor + * \param[in] fd PCAP file descriptor + * \param[in] pkt Pointer to the packet to write + * \param[in] pkt_len Valid length of the packet + * \param[in] pkt_snaplen Total length of the packet + * \param[in] tv_sec Seconds after Epoch + * \param[in] tv_usec Microseconds after Epoch + * \return Length of written packet on success, + * -1 if either the packet header or packet payload could not be written + */ + +ssize_t pcap_write(const int fd, const uint8_t * const pkt, + const size_t pkt_len, const size_t pkt_snaplen, + const uint64_t tv_sec, const uint64_t tv_usec) +{ + struct pcap_sf_pkthdr sf_hdr; + ssize_t written = 0; + + assert(fd > 0); + assert(pkt); + assert(pkt_snaplen); + + memset(&sf_hdr, 0, sizeof(sf_hdr)); + + sf_hdr.ts.tv_sec = tv_sec; + sf_hdr.ts.tv_usec = tv_usec; + sf_hdr.caplen = pkt_snaplen; + sf_hdr.len = pkt_len; + + if ((written = write(fd, &sf_hdr, sizeof(sf_hdr))) != sizeof(sf_hdr)) { + return (-1); + } + + if ((written = write(fd, pkt, sf_hdr.len)) != sf_hdr.len) { + return (-1); + } + + return (written); +} -- 2.11.4.GIT