diff options
Diffstat (limited to 'pcap_mm.c')
-rw-r--r-- | pcap_mm.c | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/pcap_mm.c b/pcap_mm.c new file mode 100644 index 0000000..f3b3fd0 --- /dev/null +++ b/pcap_mm.c @@ -0,0 +1,197 @@ +/* + * netsniff-ng - the packet sniffing beast + * Copyright 2011 - 2013 Daniel Borkmann. + * Subject to the GPL, version 2. + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdbool.h> +#include <sys/mman.h> + +#include "pcap_io.h" +#include "xio.h" +#include "xutils.h" +#include "built_in.h" + +static size_t map_size = 0; +static char *ptr_va_start, *ptr_va_curr; + +static void __pcap_mmap_write_need_remap(int fd) +{ + int ret; + off_t pos, map_size_old = map_size; + off_t offset = ptr_va_curr - ptr_va_start; + + map_size = PAGE_ALIGN(map_size_old * 10 / 8); + + pos = lseek(fd, map_size, SEEK_SET); + if (pos < 0) + panic("Cannot lseek pcap file!\n"); + + ret = write_or_die(fd, "", 1); + if (ret != 1) + panic("Cannot write file!\n"); + + ptr_va_start = mremap(ptr_va_start, map_size_old, map_size, MREMAP_MAYMOVE); + if (ptr_va_start == MAP_FAILED) + panic("mmap of file failed!"); + + ret = madvise(ptr_va_start, map_size, MADV_SEQUENTIAL); + if (ret < 0) + panic("Failed to give kernel mmap advise!\n"); + + ptr_va_curr = ptr_va_start + offset; +} + +static ssize_t pcap_mm_write(int fd, pcap_pkthdr_t *phdr, enum pcap_type type, + const uint8_t *packet, size_t len) +{ + size_t hdrsize = pcap_get_hdr_length(phdr, type); + + if ((off_t) (ptr_va_curr - ptr_va_start) + hdrsize + len > map_size) + __pcap_mmap_write_need_remap(fd); + + fmemcpy(ptr_va_curr, &phdr->raw, hdrsize); + ptr_va_curr += hdrsize; + fmemcpy(ptr_va_curr, packet, len); + ptr_va_curr += len; + + return hdrsize + len; +} + +static ssize_t pcap_mm_read(int fd, pcap_pkthdr_t *phdr, enum pcap_type type, + uint8_t *packet, size_t len) +{ + size_t hdrsize = pcap_get_hdr_length(phdr, type), hdrlen; + + if (unlikely((off_t) (ptr_va_curr + hdrsize - ptr_va_start) > map_size)) + return -EIO; + + fmemcpy(&phdr->raw, ptr_va_curr, hdrsize); + ptr_va_curr += hdrsize; + hdrlen = pcap_get_length(phdr, type); + + if (unlikely((off_t) (ptr_va_curr + hdrlen - ptr_va_start) > map_size)) + return -EIO; + if (unlikely(hdrlen == 0 || hdrlen > len)) + return -EINVAL; + + fmemcpy(packet, ptr_va_curr, hdrlen); + ptr_va_curr += hdrlen; + + return hdrsize + hdrlen; +} + +static inline off_t ____get_map_size(bool jumbo) +{ + int allocsz = jumbo ? 16 : 3; + + return PAGE_ALIGN(sizeof(struct pcap_filehdr) + (PAGE_SIZE * allocsz) * 1024); +} + +static void __pcap_mm_prepare_access_wr(int fd, bool jumbo) +{ + int ret; + off_t pos; + struct stat sb; + + map_size = ____get_map_size(jumbo); + + ret = fstat(fd, &sb); + if (ret < 0) + panic("Cannot fstat pcap file!\n"); + if (!S_ISREG (sb.st_mode)) + panic("pcap dump file is not a regular file!\n"); + + pos = lseek(fd, map_size, SEEK_SET); + if (pos < 0) + panic("Cannot lseek pcap file!\n"); + + ret = write_or_die(fd, "", 1); + if (ret != 1) + panic("Cannot write file!\n"); + + ptr_va_start = mmap(0, map_size, PROT_WRITE, MAP_SHARED, fd, 0); + if (ptr_va_start == MAP_FAILED) + panic("mmap of file failed!"); + ret = madvise(ptr_va_start, map_size, MADV_SEQUENTIAL); + if (ret < 0) + panic("Failed to give kernel mmap advise!\n"); + + ptr_va_curr = ptr_va_start + sizeof(struct pcap_filehdr); +} + +static void __pcap_mm_prepare_access_rd(int fd) +{ + int ret; + struct stat sb; + + ret = fstat(fd, &sb); + if (ret < 0) + panic("Cannot fstat pcap file!\n"); + if (!S_ISREG (sb.st_mode)) + panic("pcap dump file is not a regular file!\n"); + + map_size = sb.st_size; + ptr_va_start = mmap(0, map_size, PROT_READ, MAP_SHARED | MAP_LOCKED, fd, 0); + if (ptr_va_start == MAP_FAILED) + panic("mmap of file failed!"); + ret = madvise(ptr_va_start, map_size, MADV_SEQUENTIAL); + if (ret < 0) + panic("Failed to give kernel mmap advise!\n"); + + ptr_va_curr = ptr_va_start + sizeof(struct pcap_filehdr); +} + +static int pcap_mm_prepare_access(int fd, enum pcap_mode mode, bool jumbo) +{ + set_ioprio_be(); + + switch (mode) { + case PCAP_MODE_RD: + __pcap_mm_prepare_access_rd(fd); + break; + case PCAP_MODE_WR: + __pcap_mm_prepare_access_wr(fd, jumbo); + break; + default: + bug(); + } + + return 0; +} + +static void pcap_mm_fsync(int fd) +{ + msync(ptr_va_start, (off_t) (ptr_va_curr - ptr_va_start), MS_ASYNC); +} + +static void pcap_mm_prepare_close(int fd, enum pcap_mode mode) +{ + int ret; + + ret = munmap(ptr_va_start, map_size); + if (ret < 0) + panic("Cannot unmap the pcap file!\n"); + + if (mode == PCAP_MODE_WR) { + ret = ftruncate(fd, (off_t) (ptr_va_curr - ptr_va_start)); + if (ret) + panic("Cannot truncate the pcap file!\n"); + } +} + +const struct pcap_file_ops pcap_mm_ops = { + .pull_fhdr_pcap = pcap_generic_pull_fhdr, + .push_fhdr_pcap = pcap_generic_push_fhdr, + .prepare_access_pcap = pcap_mm_prepare_access, + .prepare_close_pcap = pcap_mm_prepare_close, + .read_pcap = pcap_mm_read, + .write_pcap = pcap_mm_write, + .fsync_pcap = pcap_mm_fsync, +}; |