4 * routines for importing tektronix k12xx *.rf5 files
6 * Copyright (c) 2005, Luis E. Garia Ontanon <luis@ontanon.org>
11 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
35 #include "file_wrappers.h"
39 #include <wsutil/str_util.h>
44 * http://www2.tek.com/cmswpt/madownload.lotr?ct=MA&cs=mpm&ci=11284&lc=EN
46 * for some information about the file format. You may have to fill in
47 * a form to download the document ("Recored File API Programmer Manual").
49 * Unfortunately, it describes an API that delivers records from an rf5
50 * file, not the raw format of an rf5 file, so, while it gives the formats
51 * of the records with various types, it does not indicate how those records
52 * are stored in the file.
55 /* #define DEBUG_K12 */
60 #include <wsutil/file_util.h>
63 char* env_file
= NULL
;
65 static unsigned int debug_level
= 0;
67 void k12_fprintf(char* fmt
, ...) {
71 vfprintf(dbg_out
, fmt
, ap
);
76 #define K12_DBG(level,args) do { if (level <= debug_level) { \
77 fprintf(dbg_out,"%s:%d: ",CAT(__FI,LE__),CAT(__LI,NE__)); \
79 fprintf(dbg_out,"\n"); \
82 void k12_hexdump(guint level
, gint64 offset
, char* label
, unsigned char* b
, unsigned int len
) {
83 static const char* c2t
[] = {
84 "00","01","02","03","04","05","06","07","08","09","0a","0b","0c","0d","0e","0f",
85 "10","11","12","13","14","15","16","17","18","19","1a","1b","1c","1d","1e","1f",
86 "20","21","22","23","24","25","26","27","28","29","2a","2b","2c","2d","2e","2f",
87 "30","31","32","33","34","35","36","37","38","39","3a","3b","3c","3d","3e","3f",
88 "40","41","42","43","44","45","46","47","48","49","4a","4b","4c","4d","4e","4f",
89 "50","51","52","53","54","55","56","57","58","59","5a","5b","5c","5d","5e","5f",
90 "60","61","62","63","64","65","66","67","68","69","6a","6b","6c","6d","6e","6f",
91 "70","71","72","73","74","75","76","77","78","79","7a","7b","7c","7d","7e","7f",
92 "80","81","82","83","84","85","86","87","88","89","8a","8b","8c","8d","8e","8f",
93 "90","91","92","93","94","95","96","97","98","99","9a","9b","9c","9d","9e","9f",
94 "a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","aa","ab","ac","ad","ae","af",
95 "b0","b1","b2","b3","b4","b5","b6","b7","b8","b9","ba","bb","bc","bd","be","bf",
96 "c0","c1","c2","c3","c4","c5","c6","c7","c8","c9","ca","cb","cc","cd","ce","cf",
97 "d0","d1","d2","d3","d4","d5","d6","d7","d8","d9","da","db","dc","dd","de","df",
98 "e0","e1","e2","e3","e4","e5","e6","e7","e8","e9","ea","eb","ec","ed","ee","ef",
99 "f0","f1","f2","f3","f4","f5","f6","f7","f8","f9","fa","fb","fc","fd","fe","ff"
103 if (debug_level
< level
) return;
105 fprintf(dbg_out
,"%s(%.8" G_GINT64_MODIFIER
"x,%.4x): ",label
,offset
,len
);
107 for (i
=0 ; i
<len
; i
++) {
110 fprintf(dbg_out
,"\n");
112 fprintf(dbg_out
," ");
114 fprintf(dbg_out
, "%s", c2t
[b
[i
]]);
117 fprintf(dbg_out
,"\n");
120 #define K12_HEXDMP(x,a,b,c,d) k12_hexdump(x,a,b,c,d)
123 #define K12_DBG(level,args) (void)0
124 #define K12_HEXDMP(x,a,b,c,d)
130 * the 32 bits .rf5 file contains:
131 * an 8 byte magic number
133 * 32bit number of records
134 * other 0x200 bytes bytes of uncharted territory
135 * 1 or more copies of the num_of_records in there
136 * the records whose first 32bits word is the length
137 * they are stuffed by one to four words every 0x2000 bytes
138 * and a 2 byte terminator FFFF
141 static const guint8 k12_file_magic
[] = { 0x00, 0x00, 0x02, 0x00 ,0x12, 0x05, 0x00, 0x10 };
145 guint32 num_of_records
; /* XXX: not sure about this */
147 GHashTable
* src_by_id
; /* k12_srcdsc_recs by input */
148 GHashTable
* src_by_name
; /* k12_srcdsc_recs by stack_name */
150 Buffer extra_info
; /* Buffer to hold per packet extra information */
153 typedef struct _k12_src_desc_t
{
158 k12_input_info_t input_info
;
163 * According to the Tektronix documentation, this value is a combination of
164 * a "group" code and a "type" code, with both being 2-byte values and
165 * with the "group" code followe by the "type" code. The "group" values
168 * 0x0001 - "data event"
169 * 0x0002 - "text or L1 event"
170 * 0x0007 - "configuration event"
172 * and the "type" values are:
175 * 0x0020 - "frame" (i.e., "an actual packet")
176 * 0x0021 - "transparent frame"
177 * 0x0022 - "bit data (TRAU frame)"
178 * 0x0024 - "used to mark the frame which is a fragment"
179 * 0x0026 - "used to mark the frame which is a fragment"
180 * 0x0028 - "used to mark the frame which is generated by the LSA"
181 * 0x002A - "used to mark the frame which is generated by the LSA"
184 * 0x0030 - "text event"
185 * 0x0031 - "L1 event"
186 * 0x0032 - "L1 event (BAI)"
187 * 0x0033 - "L1 event (VX)"
189 * configuration events:
190 * 0x0040 - Logical Data Source configuration event
191 * 0x0041 - Logical Link configuration event
193 /* so far we've seen these types of records */
194 #define K12_REC_PACKET 0x00010020 /* an actual packet */
195 #define K12_REC_SRCDSC 0x00070041 /* port-stack mapping + more, the key of the whole thing */
196 #define K12_REC_SCENARIO 0x00070040 /* what appears as the window's title */
197 #define K12_REC_STK_FILE 0x00070042 /* a dump of an stk file */
198 #define K12_REC_SRCDSC2 0x00070043 /* another port-stack mapping */
199 #define K12_REC_TEXT 0x00070044 /* a string containing something with a grammar (conditions/responses?) */
200 #define K12_REC_START 0x00020030 /* a string containing human readable start time */
201 #define K12_REC_STOP 0x00020031 /* a string containing human readable stop time */
204 * According to the Tektronix documentation, packets, i.e. "data events",
205 * have several different group/type values, which differ in the last
206 * nibble of the type code. For now, we just mask that nibble off; the
207 * format of the items are different, so we might have to treat different
208 * data event types differently.
210 #define K12_MASK_PACKET 0xfffffff0
212 /* offsets of elements in the records */
213 #define K12_RECORD_LEN 0x0 /* uint32, in bytes */
214 #define K12_RECORD_TYPE 0x4 /* uint32, see above */
215 #define K12_RECORD_FRAME_LEN 0x8 /* uint32, in bytes */
216 #define K12_RECORD_SRC_ID 0xc /* uint32 */
219 * Some records from K15 files have a port ID of an undeclared
220 * interface which happens to be the only one with the first byte changed.
221 * It is still unknown how to recognize when this happens.
222 * If the lookup of the interface record fails we'll mask it
225 #define K12_RECORD_SRC_ID_MASK 0x00ffffff
227 /* elements of packet records */
228 #define K12_PACKET_TIMESTAMP 0x18 /* int64 (8b) representing 1/2us since 01-01-1990 Z00:00:00 */
230 #define K12_PACKET_FRAME 0x20 /* start of the actual frame in the record */
232 #define K12_PACKET_OFFSET_VP 0x08 /* 2 bytes, big endian */
233 #define K12_PACKET_OFFSET_VC 0x0a /* 2 bytes, big endian */
234 #define K12_PACKET_OFFSET_CID 0x0c /* 1 byte */
236 /* elements of the source description records */
237 #define K12_SRCDESC_COLOR_FOREGROUND 0x12 /* 1 byte */
238 #define K12_SRCDESC_COLOR_BACKGROUND 0x13 /* 1 byte */
240 #define K12_SRCDESC_PORT_TYPE 0x1a /* 1 byte */
241 #define K12_SRCDESC_EXTRALEN 0x1e /* uint16, big endian */
242 #define K12_SRCDESC_NAMELEN 0x20 /* uint16, big endian */
243 #define K12_SRCDESC_STACKLEN 0x22 /* uint16, big endian */
245 #define K12_SRCDESC_EXTRATYPE 0x24 /* uint32, big endian */
246 #define K12_SRCDESC_ATM_VPI 0x38 /* uint16, big endian */
247 #define K12_SRCDESC_ATM_VCI 0x3a /* uint16, big endian */
249 #define K12_SRCDESC_ATM_AAL 0x3c /* 1 byte */
250 #define K12_SRCDESC_DS0_MASK 0x3c /* 1 byte */
254 * get_record: Get the next record into a buffer
255 * Every about 0x2000 bytes 0x10 bytes are inserted in the file,
256 * even in the middle of a record.
257 * This reads the next record without the eventual 0x10 bytes.
258 * returns the length of the record + the stuffing (if any)
260 * Returns number of bytes read on success, 0 on EOF, -1 on error;
261 * if -1 is returned, *err is set to the error indication and, for
262 * errors where that's appropriate, *err_info is set to an additional
265 * XXX: works at most with 0x1FFF bytes per record
267 static gint
get_record(guint8
** bufferp
, FILE_T fh
, gint64 file_offset
,
268 int *err
, gchar
**err_info
) {
269 static guint8
* buffer
= NULL
;
270 static guint buffer_len
= 0x2000 ;
280 /* where the next unknown 0x10 bytes are stuffed to the file */
281 guint junky_offset
= 0x2000 - (gint
) ( (file_offset
- 0x200) % 0x2000 );
283 K12_DBG(6,("get_record: ENTER: junky_offset=%" G_GINT64_MODIFIER
"d, file_offset=%" G_GINT64_MODIFIER
"d",junky_offset
,file_offset
));
285 /* no buffer is given, lets create it */
286 if (buffer
== NULL
) {
287 buffer
= (guint8
*)g_malloc(0x2000);
293 if ( junky_offset
== 0x2000 ) {
294 /* the length of the record is 0x10 bytes ahead from we are reading */
295 bytes_read
= file_read(junk
,0x14,fh
);
297 if (bytes_read
== 2 && junk
[0] == 0xff && junk
[1] == 0xff) {
298 K12_DBG(1,("get_record: EOF"));
300 } else if ( bytes_read
< 0x14 ){
301 K12_DBG(1,("get_record: SHORT READ OR ERROR"));
302 *err
= file_error(fh
, err_info
);
304 *err
= WTAP_ERR_SHORT_READ
;
309 memcpy(buffer
,&(junk
[0x10]),4);
311 /* the length of the record is right where we are reading */
312 bytes_read
= file_read(buffer
, 0x4, fh
);
314 if (bytes_read
== 2 && buffer
[0] == 0xff && buffer
[1] == 0xff) {
315 K12_DBG(1,("get_record: EOF"));
317 } else if ( bytes_read
!= 0x4 ) {
318 K12_DBG(1,("get_record: SHORT READ OR ERROR"));
319 *err
= file_error(fh
, err_info
);
321 *err
= WTAP_ERR_SHORT_READ
;
327 left
= pntohl(buffer
);
333 K12_DBG(5,("get_record: GET length=%u",left
));
335 /* XXX - Is WTAP_MAX_PACKET_SIZE */
336 if (left
< 4 || left
> WTAP_MAX_PACKET_SIZE
) {
337 K12_DBG(1,("get_record: Invalid GET length=%u",left
));
338 *err
= WTAP_ERR_BAD_FILE
;
339 *err_info
= g_strdup_printf("get_record: Invalid GET length=%u",left
);
343 while (left
> buffer_len
) *bufferp
= buffer
= (guint8
*)g_realloc(buffer
,buffer_len
*=2);
349 K12_DBG(6,("get_record: looping left=%d junky_offset=%" G_GINT64_MODIFIER
"d",left
,junky_offset
));
351 if (junky_offset
> left
) {
352 bytes_read
+= last_read
= file_read(writep
, left
, fh
);
354 if ( last_read
!= left
) {
355 K12_DBG(1,("get_record: SHORT READ OR ERROR"));
356 *err
= file_error(fh
, err_info
);
358 *err
= WTAP_ERR_SHORT_READ
;
362 K12_HEXDMP(5,file_offset
, "GOT record", buffer
, actual_len
);
366 bytes_read
+= last_read
= file_read(writep
, junky_offset
, fh
);
368 if ( last_read
!= junky_offset
) {
369 K12_DBG(1,("get_record: SHORT READ OR ERROR, read=%d expected=%d",last_read
, junky_offset
));
370 *err
= file_error(fh
, err_info
);
372 *err
= WTAP_ERR_SHORT_READ
;
379 bytes_read
+= last_read
= file_read(junk
, 0x10, fh
);
381 if ( last_read
!= 0x10 ) {
382 K12_DBG(1,("get_record: SHORT READ OR ERROR"));
383 *err
= file_error(fh
, err_info
);
385 *err
= WTAP_ERR_SHORT_READ
;
390 left
-= junky_offset
;
391 junky_offset
= 0x2000;
396 K12_HEXDMP(5,file_offset
, "GOT record", buffer
, actual_len
);
401 process_packet_data(struct wtap_pkthdr
*phdr
, Buffer
*target
, guint8
*buffer
,
402 gint len
, k12_t
*k12
)
408 k12_src_desc_t
* src_desc
;
410 phdr
->presence_flags
= WTAP_HAS_TS
;
412 ts
= pntohll(buffer
+ K12_PACKET_TIMESTAMP
);
414 phdr
->ts
.secs
= (guint32
) ((ts
/ 2000000) + 631152000);
415 phdr
->ts
.nsecs
= (guint32
) ( (ts
% 2000000) * 500 );
417 length
= pntohl(buffer
+ K12_RECORD_FRAME_LEN
) & 0x00001FFF;
418 phdr
->len
= phdr
->caplen
= length
;
420 buffer_assure_space(target
, length
);
421 memcpy(buffer_start_ptr(target
), buffer
+ K12_PACKET_FRAME
, length
);
423 /* extra information need by some protocols */
424 extra_len
= len
- K12_PACKET_FRAME
- length
;
425 buffer_assure_space(&(k12
->extra_info
), extra_len
);
426 memcpy(buffer_start_ptr(&(k12
->extra_info
)),
427 buffer
+ K12_PACKET_FRAME
+ length
, extra_len
);
428 phdr
->pseudo_header
.k12
.extra_info
= (guint8
*)buffer_start_ptr(&(k12
->extra_info
));
429 phdr
->pseudo_header
.k12
.extra_length
= extra_len
;
431 src_id
= pntohl(buffer
+ K12_RECORD_SRC_ID
);
432 K12_DBG(5,("process_packet_data: src_id=%.8x",src_id
));
433 phdr
->pseudo_header
.k12
.input
= src_id
;
435 if ( ! (src_desc
= (k12_src_desc_t
*)g_hash_table_lookup(k12
->src_by_id
,GUINT_TO_POINTER(src_id
))) ) {
437 * Some records from K15 files have a port ID of an undeclared
438 * interface which happens to be the only one with the first byte changed.
439 * It is still unknown how to recognize when this happens.
440 * If the lookup of the interface record fails we'll mask it
443 src_desc
= (k12_src_desc_t
*)g_hash_table_lookup(k12
->src_by_id
,GUINT_TO_POINTER(src_id
&K12_RECORD_SRC_ID_MASK
));
447 K12_DBG(5,("process_packet_data: input_name='%s' stack_file='%s' type=%x",src_desc
->input_name
,src_desc
->stack_file
,src_desc
->input_type
));
448 phdr
->pseudo_header
.k12
.input_name
= src_desc
->input_name
;
449 phdr
->pseudo_header
.k12
.stack_file
= src_desc
->stack_file
;
450 phdr
->pseudo_header
.k12
.input_type
= src_desc
->input_type
;
452 switch(src_desc
->input_type
) {
453 case K12_PORT_ATMPVC
:
454 if ((long)(K12_PACKET_FRAME
+ length
+ K12_PACKET_OFFSET_CID
) < len
) {
455 phdr
->pseudo_header
.k12
.input_info
.atm
.vp
= pntohs(buffer
+ K12_PACKET_FRAME
+ length
+ K12_PACKET_OFFSET_VP
);
456 phdr
->pseudo_header
.k12
.input_info
.atm
.vc
= pntohs(buffer
+ K12_PACKET_FRAME
+ length
+ K12_PACKET_OFFSET_VC
);
457 phdr
->pseudo_header
.k12
.input_info
.atm
.cid
= *((unsigned char*)(buffer
+ K12_PACKET_FRAME
+ length
+ K12_PACKET_OFFSET_CID
));
462 memcpy(&(phdr
->pseudo_header
.k12
.input_info
),&(src_desc
->input_info
),sizeof(src_desc
->input_info
));
466 K12_DBG(5,("process_packet_data: NO SRC_RECORD FOUND"));
468 memset(&(phdr
->pseudo_header
.k12
),0,sizeof(phdr
->pseudo_header
.k12
));
469 phdr
->pseudo_header
.k12
.input_name
= "unknown port";
470 phdr
->pseudo_header
.k12
.stack_file
= "unknown stack file";
473 phdr
->pseudo_header
.k12
.input
= src_id
;
474 phdr
->pseudo_header
.k12
.stuff
= k12
;
477 static gboolean
k12_read(wtap
*wth
, int *err
, gchar
**err_info
, gint64
*data_offset
) {
478 k12_t
*k12
= (k12_t
*)wth
->priv
;
479 k12_src_desc_t
* src_desc
;
480 guint8
* buffer
= NULL
;
486 offset
= file_tell(wth
->fh
);
488 /* ignore the record if it isn't a packet */
490 K12_DBG(5,("k12_read: offset=%i",offset
));
492 *data_offset
= offset
;
494 len
= get_record(&buffer
, wth
->fh
, offset
, err
, err_info
);
498 } else if (len
== 0) {
503 type
= pntohl(buffer
+ K12_RECORD_TYPE
);
504 src_id
= pntohl(buffer
+ K12_RECORD_SRC_ID
);
507 if ( ! (src_desc
= (k12_src_desc_t
*)g_hash_table_lookup(k12
->src_by_id
,GUINT_TO_POINTER(src_id
))) ) {
509 * Some records from K15 files have a port ID of an undeclared
510 * interface which happens to be the only one with the first byte changed.
511 * It is still unknown how to recognize when this happens.
512 * If the lookup of the interface record fails we'll mask it
515 src_desc
= (k12_src_desc_t
*)g_hash_table_lookup(k12
->src_by_id
,GUINT_TO_POINTER(src_id
&K12_RECORD_SRC_ID_MASK
));
518 K12_DBG(5,("k12_read: record type=%x src_id=%x",type
,src_id
));
522 } while ( ((type
& K12_MASK_PACKET
) != K12_REC_PACKET
) || !src_id
|| !src_desc
);
524 process_packet_data(&wth
->phdr
, wth
->frame_buffer
, buffer
, len
, k12
);
530 static gboolean
k12_seek_read(wtap
*wth
, gint64 seek_off
, struct wtap_pkthdr
*phdr
, Buffer
*buf
, int length _U_
, int *err
, gchar
**err_info
) {
531 k12_t
*k12
= (k12_t
*)wth
->priv
;
535 K12_DBG(5,("k12_seek_read: ENTER"));
537 if ( file_seek(wth
->random_fh
, seek_off
, SEEK_SET
, err
) == -1) {
538 K12_DBG(5,("k12_seek_read: SEEK ERROR"));
542 len
= get_record(&buffer
, wth
->random_fh
, seek_off
, err
, err_info
);
544 K12_DBG(5,("k12_seek_read: READ ERROR"));
548 K12_DBG(5,("k12_seek_read: SHORT READ"));
549 *err
= WTAP_ERR_SHORT_READ
;
553 process_packet_data(phdr
, buf
, buffer
, len
, k12
);
555 K12_DBG(5,("k12_seek_read: DONE OK"));
561 static k12_t
* new_k12_file_data(void) {
562 k12_t
* fd
= g_new(k12_t
,1);
565 fd
->num_of_records
= 0;
566 fd
->src_by_name
= g_hash_table_new(g_str_hash
,g_str_equal
);
567 fd
->src_by_id
= g_hash_table_new(g_direct_hash
,g_direct_equal
);
569 buffer_init(&(fd
->extra_info
), 100);
574 static gboolean
destroy_srcdsc(gpointer k _U_
, gpointer v
, gpointer p _U_
) {
575 k12_src_desc_t
* rec
= (k12_src_desc_t
*)v
;
577 g_free(rec
->input_name
);
578 g_free(rec
->stack_file
);
584 static void destroy_k12_file_data(k12_t
* fd
) {
585 g_hash_table_destroy(fd
->src_by_id
);
586 g_hash_table_foreach_remove(fd
->src_by_name
,destroy_srcdsc
,NULL
);
587 g_hash_table_destroy(fd
->src_by_name
);
588 buffer_free(&(fd
->extra_info
));
592 static void k12_close(wtap
*wth
) {
593 k12_t
*k12
= (k12_t
*)wth
->priv
;
595 destroy_k12_file_data(k12
);
596 wth
->priv
= NULL
; /* destroy_k12_file_data freed it */
598 K12_DBG(5,("k12_close: CLOSED"));
599 if (env_file
) fclose(dbg_out
);
604 int k12_open(wtap
*wth
, int *err
, gchar
**err_info
) {
606 guint8 header_buffer
[0x200];
619 gchar
* env_level
= getenv("K12_DEBUG_LEVEL");
620 env_file
= getenv("K12_DEBUG_FILENAME");
622 dbg_out
= ws_fopen(env_file
,"w");
623 if (dbg_out
== NULL
) {
625 K12_DBG(1,("unable to open K12 DEBUG FILENAME for writing! Logging to standard error"));
629 if ( env_level
) debug_level
= strtoul(env_level
,NULL
,10);
630 K12_DBG(1,("k12_open: ENTER debug_level=%u",debug_level
));
633 if ( file_read(header_buffer
,0x200,wth
->fh
) != 0x200 ) {
634 K12_DBG(1,("k12_open: FILE HEADER TOO SHORT OR READ ERROR"));
635 *err
= file_error(wth
->fh
, err_info
);
636 if (*err
!= 0 && *err
!= WTAP_ERR_SHORT_READ
) {
641 if ( memcmp(header_buffer
,k12_file_magic
,8) != 0 ) {
642 K12_DBG(1,("k12_open: BAD MAGIC"));
649 file_data
= new_k12_file_data();
651 file_data
->file_len
= pntohl( header_buffer
+ 0x8);
652 file_data
->num_of_records
= pntohl( header_buffer
+ 0xC );
654 K12_DBG(5,("k12_open: FILE_HEADER OK: offset=%x file_len=%i records=%i",
657 file_data
->num_of_records
));
661 len
= get_record(&read_buffer
, wth
->fh
, offset
, err
, err_info
);
664 K12_DBG(1,("k12_open: BAD HEADER RECORD",len
));
665 destroy_k12_file_data(file_data
);
669 K12_DBG(1,("k12_open: BAD HEADER RECORD",len
));
670 *err
= WTAP_ERR_SHORT_READ
;
671 destroy_k12_file_data(file_data
);
676 type
= pntohl( read_buffer
+ K12_RECORD_TYPE
);
678 if ( (type
& K12_MASK_PACKET
) == K12_REC_PACKET
) {
680 * we are at the first packet record, rewind and leave.
682 if (file_seek(wth
->fh
, offset
, SEEK_SET
, err
) == -1) {
683 destroy_k12_file_data(file_data
);
686 K12_DBG(5,("k12_open: FIRST PACKET offset=%x",offset
));
688 } else if (type
== K12_REC_SRCDSC
|| type
== K12_REC_SRCDSC2
) {
689 rec
= g_new0(k12_src_desc_t
,1);
691 rec_len
= pntohl( read_buffer
+ K12_RECORD_LEN
);
692 extra_len
= pntohs( read_buffer
+ K12_SRCDESC_EXTRALEN
);
693 name_len
= pntohs( read_buffer
+ K12_SRCDESC_NAMELEN
);
694 stack_len
= pntohs( read_buffer
+ K12_SRCDESC_STACKLEN
);
696 rec
->input
= pntohl( read_buffer
+ K12_RECORD_SRC_ID
);
698 K12_DBG(5,("k12_open: INTERFACE RECORD offset=%x interface=%x",offset
,rec
->input
));
700 if (name_len
== 0 || stack_len
== 0
701 || 0x20 + extra_len
+ name_len
+ stack_len
> rec_len
) {
703 K12_DBG(5,("k12_open: failed (name_len == 0 || stack_len == 0 "
704 "|| 0x20 + extra_len + name_len + stack_len > rec_len) extra_len=%i name_len=%i stack_len=%i"));
705 destroy_k12_file_data(file_data
);
710 switch(( rec
->input_type
= pntohl( read_buffer
+ K12_SRCDESC_EXTRATYPE
) )) {
712 rec
->input_info
.ds0mask
= 0x00000000;
714 for (i
= 0; i
< 32; i
++) {
715 rec
->input_info
.ds0mask
|= ( *(read_buffer
+ K12_SRCDESC_DS0_MASK
+ i
) == 0xff ) ? 0x1<<(31-i
) : 0x0;
719 case K12_PORT_ATMPVC
:
720 rec
->input_info
.atm
.vp
= pntohs( read_buffer
+ K12_SRCDESC_ATM_VPI
);
721 rec
->input_info
.atm
.vc
= pntohs( read_buffer
+ K12_SRCDESC_ATM_VCI
);
726 else { /* Record viewer generated files
727 don't have this information */
728 if (read_buffer
[K12_SRCDESC_PORT_TYPE
] >= 0x14
729 && read_buffer
[K12_SRCDESC_PORT_TYPE
] <= 0x17)
730 /* For ATM2_E1DS1, ATM2_E3DS3,
731 ATM2_STM1EL and ATM2_STM1OP */
732 rec
->input_type
= K12_PORT_ATMPVC
;
735 /* XXX - this is assumed, in a number of places (not just in the
736 ascii_strdown_inplace() call below) to be null-terminated;
737 is that guaranteed (even with a corrupt file)?
738 Obviously not, as a corrupt file could contain anything
739 here; the Tektronix document says the strings "must end
740 with \0", but a bad file could fail to add the \0. */
741 rec
->input_name
= (gchar
*)g_memdup(read_buffer
+ K12_SRCDESC_EXTRATYPE
+ extra_len
, name_len
);
742 rec
->stack_file
= (gchar
*)g_memdup(read_buffer
+ K12_SRCDESC_EXTRATYPE
+ extra_len
+ name_len
, stack_len
);
744 ascii_strdown_inplace (rec
->stack_file
);
746 g_hash_table_insert(file_data
->src_by_id
,GUINT_TO_POINTER(rec
->input
),rec
);
747 g_hash_table_insert(file_data
->src_by_name
,rec
->stack_file
,rec
);
757 wth
->file_type_subtype
= WTAP_FILE_TYPE_SUBTYPE_K12
;
758 wth
->file_encap
= WTAP_ENCAP_K12
;
759 wth
->snapshot_length
= 0;
760 wth
->subtype_read
= k12_read
;
761 wth
->subtype_seek_read
= k12_seek_read
;
762 wth
->subtype_close
= k12_close
;
763 wth
->priv
= (void *)file_data
;
764 wth
->tsprecision
= WTAP_FILE_TSPREC_NSEC
;
771 guint32 num_of_records
;
775 int k12_dump_can_write_encap(int encap
) {
777 if (encap
== WTAP_ENCAP_PER_PACKET
)
778 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED
;
780 if (encap
!= WTAP_ENCAP_K12
)
781 return WTAP_ERR_UNSUPPORTED_ENCAP
;
786 static const gchar dumpy_junk
[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
788 static gboolean
k12_dump_record(wtap_dumper
*wdh
, guint32 len
, guint8
* buffer
, int *err_p
) {
789 k12_dump_t
*k12
= (k12_dump_t
*)wdh
->priv
;
790 guint32 junky_offset
= (0x2000 - ( (k12
->file_offset
- 0x200) % 0x2000 )) % 0x2000;
792 if (len
> junky_offset
) {
794 if (! wtap_dump_file_write(wdh
, buffer
, junky_offset
, err_p
))
797 if (! wtap_dump_file_write(wdh
, dumpy_junk
, 0x10, err_p
))
800 if (! wtap_dump_file_write(wdh
, buffer
+junky_offset
, len
- junky_offset
, err_p
))
803 k12
->file_offset
+= len
+ 0x10;
805 if (! wtap_dump_file_write(wdh
, buffer
, len
, err_p
))
807 k12
->file_offset
+= len
;
810 k12
->num_of_records
++;
814 static void k12_dump_src_setting(gpointer k _U_
, gpointer v
, gpointer p
) {
815 k12_src_desc_t
* src_desc
= (k12_src_desc_t
*)v
;
816 wtap_dumper
*wdh
= (wtap_dumper
*)p
;
820 int errxxx
; /* dummy */
823 guint8 buffer
[0x2000];
851 guint8 unk_data
[0x10];
862 obj
.record
.type
= g_htonl(K12_REC_SRCDSC
);
863 obj
.record
.unk32_1
= g_htonl(0x00000001);
864 obj
.record
.input
= g_htonl(src_desc
->input
);
866 obj
.record
.unk32_2
= g_htons(0x0000);
867 obj
.record
.color
= g_htons(0x060f);
868 obj
.record
.unk32_3
= g_htonl(0x00000003);
869 switch (src_desc
->input_type
) {
870 case K12_PORT_ATMPVC
:
871 obj
.record
.unk32_4
= g_htonl(0x01001400);
874 obj
.record
.unk32_4
= g_htonl(0x01000100);
877 obj
.record
.unk16_1
= g_htons(0x0000);
878 obj
.record
.name_len
= (guint16
) strlen(src_desc
->input_name
) + 1;
879 obj
.record
.stack_len
= (guint16
) strlen(src_desc
->stack_file
) + 1;
881 obj
.record
.extra
.type
= g_htonl(src_desc
->input_type
);
883 switch (src_desc
->input_type
) {
884 case K12_PORT_ATMPVC
:
885 obj
.record
.extra_len
= g_htons(0x18);
886 obj
.record
.extra
.desc
.atm
.vp
= g_htons(src_desc
->input_info
.atm
.vp
);
887 obj
.record
.extra
.desc
.atm
.vc
= g_htons(src_desc
->input_info
.atm
.vc
);
891 obj
.record
.extra_len
= g_htons(0x18);
892 for( i
=0; i
<32; i
++ ) {
893 obj
.record
.extra
.desc
.ds0mask
.mask
[i
] =
894 (src_desc
->input_info
.ds0mask
& (1 << i
)) ? 0xff : 0x00;
899 obj
.record
.extra_len
= g_htons(0x08);
904 memcpy(obj
.buffer
+ offset
,
905 src_desc
->input_name
,
906 obj
.record
.name_len
);
908 memcpy(obj
.buffer
+ offset
+ obj
.record
.name_len
,
909 src_desc
->stack_file
,
910 obj
.record
.stack_len
);
912 len
= offset
+ obj
.record
.name_len
+ obj
.record
.stack_len
;
913 len
+= (len
% 4) ? 4 - (len
% 4) : 0;
915 obj
.record
.len
= g_htonl(len
);
916 obj
.record
.name_len
= g_htons(obj
.record
.name_len
);
917 obj
.record
.stack_len
= g_htons(obj
.record
.stack_len
);
919 k12_dump_record(wdh
,len
,obj
.buffer
, &errxxx
); /* fwrite errs ignored: see k12_dump below */
922 static gboolean
k12_dump(wtap_dumper
*wdh
, const struct wtap_pkthdr
*phdr
,
923 const guint8
*pd
, int *err
) {
924 const union wtap_pseudo_header
*pseudo_header
= &phdr
->pseudo_header
;
925 k12_dump_t
*k12
= (k12_dump_t
*)wdh
->priv
;
928 guint8 buffer
[0x2000];
939 guint8 frame
[0x1fc0];
943 if (k12
->num_of_records
== 0) {
944 k12_t
* file_data
= (k12_t
*)pseudo_header
->k12
.stuff
;
945 /* XXX: We'll assume that any fwrite errors in k12_dump_src_setting will */
946 /* repeat during the final k12_dump_record at the end of k12_dump */
947 /* (and thus cause an error return from k12_dump). */
948 /* (I don't see a reasonably clean way to handle any fwrite errors */
949 /* encountered in k12_dump_src_setting). */
950 g_hash_table_foreach(file_data
->src_by_id
,k12_dump_src_setting
,wdh
);
952 obj
.record
.len
= 0x20 + phdr
->len
;
953 obj
.record
.len
+= (obj
.record
.len
% 4) ? 4 - obj
.record
.len
% 4 : 0;
955 len
= obj
.record
.len
;
957 obj
.record
.len
= g_htonl(obj
.record
.len
);
959 obj
.record
.type
= g_htonl(K12_REC_PACKET
);
960 obj
.record
.frame_len
= g_htonl(phdr
->len
);
961 obj
.record
.input
= g_htonl(pseudo_header
->k12
.input
);
963 obj
.record
.ts
= GUINT64_TO_BE((((guint64
)phdr
->ts
.secs
- 631152000) * 2000000) + (phdr
->ts
.nsecs
/ 1000 * 2));
965 memcpy(obj
.record
.frame
,pd
,phdr
->len
);
967 return k12_dump_record(wdh
,len
,obj
.buffer
, err
);
970 static const guint8 k12_eof
[] = {0xff,0xff};
972 static gboolean
k12_dump_close(wtap_dumper
*wdh
, int *err
) {
973 k12_dump_t
*k12
= (k12_dump_t
*)wdh
->priv
;
975 guint8 b
[sizeof(guint32
)];
979 if (! wtap_dump_file_write(wdh
, k12_eof
, 2, err
))
982 if (wtap_dump_file_seek(wdh
, 8, SEEK_SET
, err
) == -1)
985 d
.u
= g_htonl(k12
->file_len
);
987 if (! wtap_dump_file_write(wdh
, d
.b
, 4, err
))
990 d
.u
= g_htonl(k12
->num_of_records
);
992 if (! wtap_dump_file_write(wdh
, d
.b
, 4, err
))
999 gboolean
k12_dump_open(wtap_dumper
*wdh
, int *err
) {
1002 if ( ! wtap_dump_file_write(wdh
, k12_file_magic
, 8, err
)) {
1006 if (wtap_dump_file_seek(wdh
, 0x200, SEEK_SET
, err
) == -1)
1009 wdh
->subtype_write
= k12_dump
;
1010 wdh
->subtype_close
= k12_dump_close
;
1012 k12
= (k12_dump_t
*)g_malloc(sizeof(k12_dump_t
));
1013 wdh
->priv
= (void *)k12
;
1014 k12
->file_len
= 0x200;
1015 k12
->num_of_records
= 0;
1016 k12
->file_offset
= 0x200;