1 /* -*- c -*- */
2 /*
3 * Copyright 2014 Christopher D. Kilgour techie AT whiterocker.com
4 *
5 * This file is part of libbtbb
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2, or (at your option)
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with libbtbb; see the file COPYING. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street,
20 * Boston, MA 02110-1301, USA.
21 */
22
23 #include "pcapng.h"
24
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <string.h>
28 #include <sys/mman.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <unistd.h>
32
33 static option_header padopt = {
34 .option_code = 0xffff,
35 };
36
pcapng_create(PCAPNG_HANDLE * handle,const char * filename,const option_header * section_options,const size_t section_options_space,const uint16_t link_type,const uint32_t snaplen,const option_header * interface_options,const size_t interface_options_space)37 PCAPNG_RESULT pcapng_create( PCAPNG_HANDLE * handle,
38 const char * filename,
39 const option_header * section_options,
40 const size_t section_options_space,
41 const uint16_t link_type,
42 const uint32_t snaplen,
43 const option_header * interface_options,
44 const size_t interface_options_space )
45 {
46 PCAPNG_RESULT retval = PCAPNG_OK;
47 int PGSZ = getpagesize( );
48 size_t zeroes = 0;
49 ssize_t result = -1;
50
51 handle->section_header = NULL;
52 handle->interface_description = NULL;
53 handle->section_header_size = handle->next_section_option_offset =
54 handle->interface_description_size =
55 handle->next_interface_option_offset = 0;
56
57 handle->fd = open( filename, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP );
58 if (handle->fd == -1) {
59 switch( errno ) {
60 case EEXIST:
61 retval = PCAPNG_FILE_EXISTS;
62 break;
63 case EMFILE:
64 case ENFILE:
65 retval = PCAPNG_TOO_MANY_FILES_OPEN;
66 break;
67 case ENOMEM:
68 case ENOSPC:
69 retval = PCAPNG_NO_MEMORY;
70 break;
71 default:
72 retval = PCAPNG_FILE_NOT_ALLOWED;
73 }
74 }
75
76 if (retval == PCAPNG_OK) {
77 /* section header */
78 const section_header_block shb = {
79 .block_type = BLOCK_TYPE_SECTION_HEADER,
80 .block_total_length = 28,
81 .byte_order_magic = SECTION_HEADER_BYTE_ORDER_MAGIC,
82 .major_version = 1,
83 .minor_version = 0,
84 .section_length = (uint64_t) -1,
85 };
86 handle->section_header_size = sizeof( shb );
87 result = write( handle->fd, &shb, sizeof( shb ) );
88 /* write initial section options */
89 while ((result != -1) &&
90 section_options &&
91 section_options->option_code &&
92 section_options->option_length) {
93 size_t paddedsz = 4*((section_options->option_length+3)/4);
94 zeroes = paddedsz - section_options->option_length;
95 result = write( handle->fd, section_options, 4+section_options->option_length );
96 while ((zeroes > 0) && (result != -1)) {
97 result = write( handle->fd, "\0", 1 );
98 zeroes--;
99 }
100 section_options = (const option_header *) &((uint8_t *)section_options)[4+paddedsz];
101 handle->section_header_size += (4+paddedsz);
102 }
103 handle->next_section_option_offset = handle->section_header_size;
104 }
105
106 if (result == -1) {
107 retval = PCAPNG_FILE_WRITE_ERROR;
108 }
109 else {
110 /* determine the size of section header with desired space */
111 zeroes = (size_t) PGSZ*((handle->section_header_size + 4 +
112 section_options_space + PGSZ - 1)/PGSZ) -
113 handle->section_header_size;
114 handle->section_header_size += zeroes;
115 while ((zeroes > 0) && (result != -1)) {
116 result = write( handle->fd, "\0", 1 );
117 zeroes--;
118 }
119
120 /* mmap the section header */
121 handle->section_header = mmap( NULL, handle->section_header_size,
122 PROT_READ|PROT_WRITE,
123 MAP_SHARED,
124 handle->fd, 0 );
125 }
126
127 if (retval == PCAPNG_OK) {
128 if (result == -1) {
129 retval = PCAPNG_FILE_WRITE_ERROR;
130 }
131 else if (handle->section_header == MAP_FAILED) {
132 retval = PCAPNG_MMAP_FAILED;
133 }
134 else {
135 /* write the interface header */
136 const interface_description_block idb = {
137 .block_type = BLOCK_TYPE_INTERFACE,
138 .block_total_length = 0,
139 .link_type = link_type,
140 .snaplen = snaplen
141 };
142 handle->interface_description_size = sizeof( idb );
143 result = write( handle->fd, &idb, sizeof( idb ) );
144
145 /* write interface options */
146 while ((result != -1) &&
147 interface_options &&
148 interface_options->option_code &&
149 interface_options->option_length) {
150 size_t paddedsz = 4*((interface_options->option_length+3)/4);
151 zeroes = paddedsz - interface_options->option_length;
152 result = write( handle->fd, interface_options, 4+interface_options->option_length );
153 while ((zeroes > 0) && (result != -1)) {
154 result = write( handle->fd, "\0", 1 );
155 zeroes--;
156 }
157 interface_options = (const option_header *) &((uint8_t *)interface_options)[4+paddedsz];
158 handle->interface_description_size += (4+paddedsz);
159 }
160 handle->next_interface_option_offset = handle->interface_description_size;
161 }
162 }
163
164 if (retval == PCAPNG_OK) {
165 if (result == -1) {
166 retval = PCAPNG_FILE_WRITE_ERROR;
167 }
168 else {
169 /* determine the size of interface description with desired space */
170 zeroes = (size_t) PGSZ*((handle->interface_description_size + 4 +
171 interface_options_space + PGSZ - 1)/PGSZ) -
172 handle->interface_description_size;
173 handle->interface_description_size += zeroes;
174 while ((zeroes > 0) && (result != -1)) {
175 result = write( handle->fd, "\0", 1 );
176 zeroes--;
177 }
178
179 /* mmap the interface description */
180 handle->interface_description = mmap( NULL, handle->interface_description_size,
181 PROT_READ|PROT_WRITE,
182 MAP_SHARED,
183 handle->fd,
184 handle->section_header_size );
185 }
186 }
187
188 if (retval == PCAPNG_OK) {
189 if (result == -1) {
190 retval = PCAPNG_FILE_WRITE_ERROR;
191 }
192 else if (handle->interface_description == MAP_FAILED) {
193 retval = PCAPNG_MMAP_FAILED;
194 }
195 else {
196 uint8_t * dest = &((uint8_t *)handle->section_header)[handle->next_section_option_offset];
197 padopt.option_length = handle->section_header_size -
198 handle->next_section_option_offset - 12;
199
200 /* Add padding options, update the header sizes. */
201 (void) memcpy( dest, &padopt, sizeof( padopt ) );
202 handle->section_header->block_total_length =
203 (uint32_t) handle->section_header_size;
204 ((uint32_t*)handle->section_header)[handle->section_header_size/4-1] =
205 (uint32_t) handle->section_header_size;
206
207 padopt.option_length = handle->interface_description_size -
208 handle->next_interface_option_offset - 12;
209 dest = &((uint8_t *)handle->interface_description)[handle->next_interface_option_offset];
210 (void) memcpy( dest, &padopt, sizeof( padopt ) );
211 handle->interface_description->block_total_length =
212 (uint32_t) handle->interface_description_size;
213 ((uint32_t*)handle->interface_description)[handle->interface_description_size/4-1] =
214 (uint32_t) handle->interface_description_size;
215
216 handle->section_header->section_length = (uint64_t) handle->interface_description_size;
217 }
218 }
219
220 if (retval != PCAPNG_OK) {
221 (void) pcapng_close( handle );
222 }
223
224 return retval;
225 }
226
pcapng_append_section_option(PCAPNG_HANDLE * handle,const option_header * section_option)227 PCAPNG_RESULT pcapng_append_section_option( PCAPNG_HANDLE * handle,
228 const option_header * section_option )
229 {
230 PCAPNG_RESULT retval = PCAPNG_OK;
231 if (handle && (handle->fd != -1)) {
232 if (handle->section_header &&
233 (handle->section_header != MAP_FAILED) &&
234 handle->next_section_option_offset &&
235 section_option) {
236 size_t copysz = 4+section_option->option_length;
237 uint8_t * dest = &((uint8_t *)handle->section_header)[handle->next_section_option_offset];
238 (void) memcpy( dest, section_option, copysz );
239 handle->next_section_option_offset += 4*((copysz+3)/4);
240
241 /* update padding option */
242 dest = &((uint8_t *)handle->section_header)[handle->next_section_option_offset];
243 padopt.option_length = handle->section_header_size -
244 handle->next_section_option_offset - 12;
245 (void) memcpy( dest, &padopt, sizeof( padopt ) );
246 }
247 else {
248 retval = PCAPNG_NO_MEMORY;
249 }
250 }
251 else {
252 retval = PCAPNG_INVALID_HANDLE;
253 }
254 return retval;
255 }
256
pcapng_append_interface_option(PCAPNG_HANDLE * handle,const option_header * interface_option)257 PCAPNG_RESULT pcapng_append_interface_option( PCAPNG_HANDLE * handle,
258 const option_header * interface_option )
259 {
260 PCAPNG_RESULT retval = PCAPNG_OK;
261 if (handle && (handle->fd != -1)) {
262 if (handle->interface_description &&
263 (handle->interface_description != MAP_FAILED) &&
264 handle->next_interface_option_offset &&
265 interface_option) {
266 size_t copysz = 4+interface_option->option_length;
267 uint8_t * dest = &((uint8_t *)handle->interface_description)[handle->next_interface_option_offset];
268 (void) memcpy( dest, interface_option, copysz );
269 handle->next_interface_option_offset += 4*((copysz+3)/4);
270
271 /* update padding option */
272 dest = &((uint8_t *)handle->interface_description)[handle->next_interface_option_offset];
273 padopt.option_length = handle->interface_description_size -
274 handle->next_interface_option_offset - 12;
275 (void) memcpy( dest, &padopt, sizeof( padopt ) );
276 }
277 else {
278 retval = PCAPNG_NO_MEMORY;
279 }
280 }
281 else {
282 retval = PCAPNG_INVALID_HANDLE;
283 }
284 return retval;
285 }
286
pcapng_append_packet(PCAPNG_HANDLE * handle,const enhanced_packet_block * packet)287 PCAPNG_RESULT pcapng_append_packet( PCAPNG_HANDLE * handle,
288 const enhanced_packet_block * packet )
289 {
290 PCAPNG_RESULT retval = PCAPNG_OK;
291 if (handle && (handle->fd != -1)) {
292 size_t writesz = packet->block_total_length;
293 ssize_t result = write( handle->fd, packet, writesz );
294 if (result == -1) {
295 result = PCAPNG_FILE_WRITE_ERROR;
296 }
297 else {
298 handle->section_header->section_length += writesz;
299 }
300 }
301 else {
302 retval = PCAPNG_INVALID_HANDLE;
303 }
304 return retval;
305 }
306
pcapng_close(PCAPNG_HANDLE * handle)307 PCAPNG_RESULT pcapng_close( PCAPNG_HANDLE * handle )
308 {
309 if (handle->interface_description &&
310 (handle->interface_description != MAP_FAILED)) {
311 (void) munmap( handle->interface_description,
312 handle->interface_description_size );
313 }
314 if (handle->section_header &&
315 (handle->section_header != MAP_FAILED)) {
316 (void) munmap( handle->section_header,
317 handle->section_header_size );
318 }
319 if (handle->fd != -1) {
320 (void) close( handle->fd );
321 }
322 return PCAPNG_OK;
323 }
324