xref: /libbtbb/lib/src/pcapng.c (revision 25d64f63a355f4c01d10cf4f69da1c2246b040d9)
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;
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 
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 
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 
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 
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