netsniff-ng: pcap: fix multi-file dump with dropped privs
[netsniff-ng.git] / src / pcap_mmap.c
blob943ba020d8e78a6d4f59ddf881f54593422482f5
1 /*
2 * netsniff-ng - the packet sniffing beast
3 * By Daniel Borkmann <daniel@netsniff-ng.org>
4 * Copyright 2011 Daniel Borkmann.
5 * Subject to the GPL, version 2.
6 */
8 #define _GNU_SOURCE
9 #include <stdio.h>
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <fcntl.h>
13 #include <unistd.h>
14 #include <sys/mman.h>
16 #include "pcap.h"
17 #include "xio.h"
18 #include "xutils.h"
19 #include "locking.h"
20 #include "built_in.h"
22 #define DEFAULT_SLOTS 1000
24 static struct spinlock lock;
25 static off_t map_size = 0;
26 static char *pstart, *pcurr;
27 static int jumbo_frames = 0;
29 static inline off_t get_map_size(void)
31 int allocsz = jumbo_frames ? 16 : 3;
32 return PAGE_ALIGN(sizeof(struct pcap_filehdr) +
33 (PAGE_SIZE * allocsz) * DEFAULT_SLOTS);
36 static int pcap_mmap_pull_file_header(int fd, uint32_t *linktype)
38 ssize_t ret;
39 struct pcap_filehdr hdr;
41 ret = read(fd, &hdr, sizeof(hdr));
42 if (unlikely(ret != sizeof(hdr)))
43 return -EIO;
45 pcap_validate_header(&hdr);
47 *linktype = hdr.linktype;
49 return 0;
52 static int pcap_mmap_push_file_header(int fd, uint32_t linktype)
54 ssize_t ret;
55 struct pcap_filehdr hdr;
57 fmemset(&hdr, 0, sizeof(hdr));
58 pcap_prepare_header(&hdr, linktype, 0, PCAP_DEFAULT_SNAPSHOT_LEN);
60 ret = write_or_die(fd, &hdr, sizeof(hdr));
61 if (unlikely(ret != sizeof(hdr))) {
62 whine("Failed to write pkt file header!\n");
63 return -EIO;
66 return 0;
69 static int pcap_mmap_prepare_writing_pcap(int fd)
71 int ret;
72 off_t pos;
73 struct stat sb;
75 spinlock_lock(&lock);
77 map_size = get_map_size();
79 ret = fstat(fd, &sb);
80 if (ret < 0)
81 panic("Cannot fstat pcap file!\n");
82 if (!S_ISREG (sb.st_mode))
83 panic("pcap dump file is not a regular file!\n");
85 pos = lseek(fd, map_size, SEEK_SET);
86 if (pos < 0)
87 panic("Cannot lseek pcap file!\n");
89 ret = write_or_die(fd, "", 1);
90 if (ret != 1)
91 panic("Cannot write file!\n");
93 pstart = mmap(0, map_size, PROT_WRITE, MAP_SHARED
94 /*| MAP_HUGETLB*/, fd, 0);
95 if (pstart == MAP_FAILED)
96 panic("mmap of file failed!");
98 ret = madvise(pstart, map_size, MADV_SEQUENTIAL);
99 if (ret < 0)
100 panic("Failed to give kernel mmap advise!\n");
102 pcurr = pstart + sizeof(struct pcap_filehdr);
104 spinlock_unlock(&lock);
106 return 0;
109 static ssize_t pcap_mmap_write_pcap_pkt(int fd, struct pcap_pkthdr *hdr,
110 uint8_t *packet, size_t len)
112 int ret;
113 off_t pos;
115 spinlock_lock(&lock);
117 if ((off_t) (pcurr - pstart) + sizeof(*hdr) + len > map_size) {
118 off_t map_size_old = map_size;
119 off_t offset = (pcurr - pstart);
121 map_size = PAGE_ALIGN(map_size_old * 10 / 8);
123 pos = lseek(fd, map_size, SEEK_SET);
124 if (pos < 0)
125 panic("Cannot lseek pcap file!\n");
127 ret = write_or_die(fd, "", 1);
128 if (ret != 1)
129 panic("Cannot write file!\n");
131 pstart = mremap(pstart, map_size_old, map_size, MREMAP_MAYMOVE);
132 if (pstart == MAP_FAILED)
133 panic("mmap of file failed!");
135 ret = madvise(pstart, map_size, MADV_SEQUENTIAL);
136 if (ret < 0)
137 panic("Failed to give kernel mmap advise!\n");
139 pcurr = pstart + offset;
142 fmemcpy(pcurr, hdr, sizeof(*hdr));
143 pcurr += sizeof(*hdr);
145 fmemcpy(pcurr, packet, len);
146 pcurr += len;
148 spinlock_unlock(&lock);
150 return sizeof(*hdr) + len;
153 static int pcap_mmap_prepare_reading_pcap(int fd)
155 int ret;
156 struct stat sb;
158 spinlock_lock(&lock);
160 ret = fstat(fd, &sb);
161 if (ret < 0)
162 panic("Cannot fstat pcap file!\n");
164 if (!S_ISREG (sb.st_mode))
165 panic("pcap dump file is not a regular file!\n");
167 map_size = sb.st_size;
169 pstart = mmap(0, map_size, PROT_READ, MAP_SHARED | MAP_LOCKED
170 /*| MAP_HUGETLB*/, fd, 0);
171 if (pstart == MAP_FAILED)
172 panic("mmap of file failed!");
174 ret = madvise(pstart, map_size, MADV_SEQUENTIAL);
175 if (ret < 0)
176 panic("Failed to give kernel mmap advise!\n");
178 pcurr = pstart + sizeof(struct pcap_filehdr);
180 spinlock_unlock(&lock);
182 return 0;
185 static ssize_t pcap_mmap_read_pcap_pkt(int fd, struct pcap_pkthdr *hdr,
186 uint8_t *packet, size_t len)
188 ssize_t ret;
189 spinlock_lock(&lock);
191 if (unlikely((off_t) (pcurr + sizeof(*hdr) - pstart) > map_size)) {
192 spinlock_unlock(&lock);
193 return -ENOMEM;
196 fmemcpy(hdr, pcurr, sizeof(*hdr));
197 pcurr += sizeof(*hdr);
199 if (unlikely((off_t) (pcurr + hdr->caplen - pstart) > map_size)) {
200 ret = -ENOMEM;
201 goto out_err;
204 if (unlikely(hdr->caplen == 0 || hdr->caplen > len)) {
205 ret = -EINVAL; /* Bogus packet */
206 goto out_err;
209 fmemcpy(packet, pcurr, hdr->caplen);
210 pcurr += hdr->caplen;
212 spinlock_unlock(&lock);
214 return sizeof(*hdr) + hdr->caplen;
216 out_err:
217 spinlock_unlock(&lock);
218 return ret;
221 static void pcap_mmap_fsync_pcap(int fd)
223 spinlock_lock(&lock);
225 msync(pstart, (off_t) (pcurr - pstart), MS_ASYNC);
227 spinlock_unlock(&lock);
230 static void pcap_mmap_prepare_close_pcap(int fd, enum pcap_mode mode)
232 int ret;
234 spinlock_lock(&lock);
236 ret = munmap(pstart, map_size);
237 if (ret < 0)
238 panic("Cannot unmap the pcap file!\n");
240 if (mode == PCAP_MODE_WRITE) {
241 ret = ftruncate(fd, (off_t) (pcurr - pstart));
242 if (ret)
243 panic("Cannot truncate the pcap file!\n");
246 spinlock_unlock(&lock);
249 const struct pcap_file_ops pcap_mmap_ops = {
250 .name = "mmap",
251 .pull_file_header = pcap_mmap_pull_file_header,
252 .push_file_header = pcap_mmap_push_file_header,
253 .prepare_writing_pcap = pcap_mmap_prepare_writing_pcap,
254 .write_pcap_pkt = pcap_mmap_write_pcap_pkt,
255 .prepare_reading_pcap = pcap_mmap_prepare_reading_pcap,
256 .read_pcap_pkt = pcap_mmap_read_pcap_pkt,
257 .fsync_pcap = pcap_mmap_fsync_pcap,
258 .prepare_close_pcap = pcap_mmap_prepare_close_pcap,
261 int init_pcap_mmap(int jumbo_support)
263 spinlock_init(&lock);
265 jumbo_frames = jumbo_support;
267 set_ioprio_be();
269 return pcap_ops_group_register(&pcap_mmap_ops, PCAP_OPS_MMAP);
272 void cleanup_pcap_mmap(void)
274 spinlock_destroy(&lock);
276 pcap_ops_group_unregister(PCAP_OPS_MMAP);