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