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