xref: /libbtbb/lib/src/pcapng.c (revision ddbdc52a7b13ca1c5e42c5475c3ed2c6395c5b5b)
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 
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, result = -1;
49 
50 	handle->section_header = NULL;
51 	handle->interface_description = NULL;
52 	handle->section_header_size = handle->next_section_option_offset =
53 		handle->interface_description_size =
54 		handle->next_interface_option_offset = 0;
55 
56 	handle->fd = open( filename, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP );
57 	if (handle->fd == -1) {
58 		switch( errno ) {
59 		case EEXIST:
60 			retval = PCAPNG_FILE_EXISTS;
61 			break;
62 		case EMFILE:
63 		case ENFILE:
64 			retval = PCAPNG_TOO_MANY_FILES_OPEN;
65 			break;
66 		case ENOMEM:
67 		case ENOSPC:
68 			retval = PCAPNG_NO_MEMORY;
69 			break;
70 		default:
71 			retval = PCAPNG_FILE_NOT_ALLOWED;
72 		}
73 	}
74 
75 	if (retval == PCAPNG_OK) {
76 		/* section header */
77 		const section_header_block shb = {
78 			.block_type = BLOCK_TYPE_SECTION_HEADER,
79 			.block_total_length = 28,
80 			.byte_order_magic = SECTION_HEADER_BYTE_ORDER_MAGIC,
81 			.major_version = 1,
82 			.minor_version = 0,
83 			.section_length = (uint64_t) -1,
84 		};
85 		handle->section_header_size = sizeof( shb );
86 		result = write( handle->fd, &shb, sizeof( shb ) );
87 		/* write initial section options */
88 		while ((result != -1) &&
89 		       section_options &&
90 		       section_options->option_code &&
91 		       section_options->option_length) {
92 			size_t paddedsz = 4*((section_options->option_length+3)/4);
93 			zeroes = paddedsz - section_options->option_length;
94 			result = write( handle->fd, section_options, 4+section_options->option_length );
95 			while ((zeroes > 0) && (result != -1)) {
96 				result = write( handle->fd, "\0", 1 );
97 				zeroes--;
98 			}
99 			section_options = (const option_header *) &((uint8_t *)section_options)[4+paddedsz];
100 			handle->section_header_size += (4+paddedsz);
101 		}
102 		handle->next_section_option_offset = handle->section_header_size;
103 	}
104 
105 	if (result == -1) {
106 		retval = PCAPNG_FILE_WRITE_ERROR;
107 	}
108 	else {
109 		/* determine the size of section header with desired space */
110 		zeroes = (size_t) PGSZ*((handle->section_header_size + 4 +
111 					 section_options_space + PGSZ - 1)/PGSZ) -
112 			handle->section_header_size;
113 		handle->section_header_size += zeroes;
114 		while ((zeroes > 0) && (result != -1)) {
115 			result = write( handle->fd, "\0", 1 );
116 			zeroes--;
117 		}
118 
119 		/* mmap the section header */
120 		handle->section_header = mmap( NULL, handle->section_header_size,
121 					       PROT_READ|PROT_WRITE,
122 					       MAP_SHARED,
123 					       handle->fd, 0 );
124 	}
125 
126 	if (retval == PCAPNG_OK) {
127 		if (result == -1) {
128 			retval = PCAPNG_FILE_WRITE_ERROR;
129 		}
130 		else if (handle->section_header == MAP_FAILED) {
131 			retval = PCAPNG_MMAP_FAILED;
132 		}
133 		else {
134 			/* write the interface header */
135 			const interface_description_block idb = {
136 				.block_type = BLOCK_TYPE_INTERFACE,
137 				.block_total_length = 0,
138 				.link_type = link_type,
139 				.snaplen = snaplen
140 			};
141 			handle->interface_description_size = sizeof( idb );
142 			result = write( handle->fd, &idb, sizeof( idb ) );
143 
144 			/* write interface options */
145 			while ((result != -1) &&
146 			       interface_options &&
147 			       interface_options->option_code &&
148 			       interface_options->option_length) {
149 				size_t paddedsz = 4*((interface_options->option_length+3)/4);
150 				zeroes = paddedsz - interface_options->option_length;
151 				result = write( handle->fd, interface_options, 4+interface_options->option_length );
152 				while ((zeroes > 0) && (result != -1)) {
153 					result = write( handle->fd, "\0", 1 );
154 					zeroes--;
155 				}
156 				interface_options = (const option_header *) &((uint8_t *)interface_options)[4+paddedsz];
157 				handle->interface_description_size += (4+paddedsz);
158 			}
159 			handle->next_interface_option_offset = handle->interface_description_size;
160 		}
161 	}
162 
163 	if (retval == PCAPNG_OK) {
164 		if (result == -1) {
165 			retval = PCAPNG_FILE_WRITE_ERROR;
166 		}
167 		else {
168 			/* determine the size of interface description with desired space */
169 			zeroes = (size_t) PGSZ*((handle->interface_description_size + 4 +
170 						 interface_options_space + PGSZ - 1)/PGSZ) -
171 				handle->interface_description_size;
172 			handle->interface_description_size += zeroes;
173 			while ((zeroes > 0) && (result != -1)) {
174 				result = write( handle->fd, "\0", 1 );
175 				zeroes--;
176 			}
177 
178 			/* mmap the interface description */
179 			handle->interface_description = mmap( NULL, handle->interface_description_size,
180 							      PROT_READ|PROT_WRITE,
181 							      MAP_SHARED,
182 							      handle->fd,
183 							      handle->section_header_size );
184 		}
185 	}
186 
187 	if (retval == PCAPNG_OK) {
188 		if (result == -1) {
189 			retval = PCAPNG_FILE_WRITE_ERROR;
190 		}
191 		else if (handle->interface_description == MAP_FAILED) {
192 			retval = PCAPNG_MMAP_FAILED;
193 		}
194 		else {
195 			uint8_t * dest = &((uint8_t *)handle->section_header)[handle->next_section_option_offset];
196 			padopt.option_length = handle->section_header_size -
197 				handle->next_section_option_offset - 12;
198 
199 			/* Add padding options, update the header sizes. */
200 			(void) memcpy( dest, &padopt, sizeof( padopt ) );
201 			handle->section_header->block_total_length =
202 				(uint32_t) handle->section_header_size;
203 			((uint32_t*)handle->section_header)[handle->section_header_size/4-1] =
204 				(uint32_t) handle->section_header_size;
205 
206 			padopt.option_length = handle->interface_description_size -
207 				handle->next_interface_option_offset - 12;
208 			dest = &((uint8_t *)handle->interface_description)[handle->next_interface_option_offset];
209 			(void) memcpy( dest, &padopt, sizeof( padopt ) );
210 			handle->interface_description->block_total_length =
211 				(uint32_t) handle->interface_description_size;
212 			((uint32_t*)handle->interface_description)[handle->interface_description_size/4-1] =
213 				(uint32_t) handle->interface_description_size;
214 
215 			handle->section_header->section_length = (uint64_t) handle->interface_description_size;
216 		}
217 	}
218 
219 	if (retval != PCAPNG_OK) {
220 		(void) pcapng_close( handle );
221 	}
222 
223 	return retval;
224 }
225 
226 PCAPNG_RESULT pcapng_append_section_option( PCAPNG_HANDLE * handle,
227 					    const option_header * section_option )
228 {
229 	PCAPNG_RESULT retval = PCAPNG_OK;
230 	if (handle && (handle->fd != -1)) {
231 		if (handle->section_header &&
232 		    (handle->section_header != MAP_FAILED) &&
233 		    handle->next_section_option_offset &&
234 		    section_option) {
235 			size_t copysz = 4+section_option->option_length;
236 			uint8_t * dest = &((uint8_t *)handle->section_header)[handle->next_section_option_offset];
237 			(void) memcpy( dest, section_option, copysz );
238 			handle->next_section_option_offset += 4*((copysz+3)/4);
239 
240 			/* update padding option */
241 			dest = &((uint8_t *)handle->section_header)[handle->next_section_option_offset];
242 			padopt.option_length = handle->section_header_size -
243 				handle->next_section_option_offset - 12;
244 			(void) memcpy( dest, &padopt, sizeof( padopt ) );
245 		}
246 		else {
247 			retval = PCAPNG_NO_MEMORY;
248 		}
249 	}
250 	else {
251 		retval = PCAPNG_INVALID_HANDLE;
252 	}
253 	return retval;
254 }
255 
256 PCAPNG_RESULT pcapng_append_interface_option( PCAPNG_HANDLE * handle,
257 					      const option_header * interface_option )
258 {
259 	PCAPNG_RESULT retval = PCAPNG_OK;
260 	if (handle && (handle->fd != -1)) {
261 		if (handle->interface_description &&
262 		    (handle->interface_description != MAP_FAILED) &&
263 		    handle->next_interface_option_offset &&
264 		    interface_option) {
265 			size_t copysz = 4+interface_option->option_length;
266 			uint8_t * dest = &((uint8_t *)handle->interface_description)[handle->next_interface_option_offset];
267 			(void) memcpy( dest, interface_option, copysz );
268 			handle->next_interface_option_offset += 4*((copysz+3)/4);
269 
270 			/* update padding option */
271 			dest = &((uint8_t *)handle->interface_description)[handle->next_interface_option_offset];
272 			padopt.option_length = handle->interface_description_size -
273 				handle->next_interface_option_offset - 12;
274 			(void) memcpy( dest, &padopt, sizeof( padopt ) );
275 		}
276 		else {
277 			retval = PCAPNG_NO_MEMORY;
278 		}
279 	}
280 	else {
281 		retval = PCAPNG_INVALID_HANDLE;
282 	}
283 	return retval;
284 }
285 
286 PCAPNG_RESULT pcapng_append_packet( PCAPNG_HANDLE * handle,
287 				    const enhanced_packet_block * packet )
288 {
289 	PCAPNG_RESULT retval = PCAPNG_OK;
290 	if (handle && (handle->fd != -1)) {
291 		size_t writesz = packet->block_total_length;
292 		ssize_t result = write( handle->fd, packet, writesz );
293 		if (result == -1) {
294 			result = PCAPNG_FILE_WRITE_ERROR;
295 		}
296 		else {
297 			handle->section_header->section_length += writesz;
298 		}
299 	}
300 	else {
301 		retval = PCAPNG_INVALID_HANDLE;
302 	}
303 	return retval;
304 }
305 
306 PCAPNG_RESULT pcapng_close( PCAPNG_HANDLE * handle )
307 {
308 	if (handle->interface_description &&
309 	    (handle->interface_description != MAP_FAILED)) {
310 		(void) munmap( handle->interface_description,
311 			       handle->interface_description_size );
312 	}
313 	if (handle->section_header &&
314 	    (handle->section_header != MAP_FAILED)) {
315 		(void) munmap( handle->section_header,
316 			       handle->section_header_size );
317 	}
318 	if (handle->fd != -1) {
319 		(void) close( handle->fd );
320 	}
321 	return PCAPNG_OK;
322 }
323