6 * Copyright (c) 2005 by Martin Warnes <Martin_Warnes@Stercomm.com>
8 * Based on toshiba.c and vms.c
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 * This module will read the contents of the iSeries (OS/400) Communication trace
27 * Both ASCII & Unicode formatted traces are supported.
29 * iSeries Comms traces consist of a header page and a subsequent number of packet records
31 * The header page contains details on the options set during running of the trace,
32 * currently the following options are a requirement for this module:
34 * 1. Object protocol = ETHERNET (Default)
35 * 2. ASCII or UNICODE file formats.
37 * The above can be acheived by passing option ASCII(*YES) with the trace command
41 /* iSeries header page
43 COMMUNICATIONS TRACE Title: OS400 - OS400 trace 10/28/05 11:44:50 Page: 1
44 Trace Description . . . . . : OS400 - OS400 trace
45 Configuration object . . . . : ETH0
46 Type . . . . . . . . . . . . : 1 1=Line, 2=Network Interface
48 Object protocol . . . . . . : ETHERNET
49 Start date/Time . . . . . . : 10/28/05 11:43:00.341
50 End date/Time . . . . . . . : 10/28/05 11:44:22.148
51 Bytes collected . . . . . . : 11999
52 Buffer size . . . . . . . . : 2048 kilobytes
53 Data direction . . . . . . . : 3 1=Sent, 2=Received, 3=Both
54 Stop on buffer full . . . . : Y Y=Yes, N=No
55 Number of bytes to trace
56 Beginning bytes . . . . . : *MAX Value, *CALC, *MAX
57 Ending bytes . . . . . . : *CALC Value, *CALC
58 Controller name . . . . . . : *ALL *ALL, name
59 Data representation . . . . : 1 1=ASCII, 2=EBCDIC, 3=*CALC
60 Format SNA data only . . . . : N Y=Yes, N=No
61 Format RR, RNR commands . . : N Y=Yes, N=No
62 Format TCP/IP data only . . : Y Y=Yes, N=No
63 IP address . . . . . . . . : *ALL *ALL, address
64 IP address . . . . . . . . : *ALL *ALL, address
65 IP port . . . . . . . . . : *ALL *ALL, IP port
66 Format UI data only . . . . : N Y=Yes, N=No
67 Select Ethernet data . . . . : 3 1=802.3, 2=ETHV2, 3=Both
68 Format Broadcast data . . . : Y Y=Yes, N=No
71 /* iSeries formatted packet records consist of a header line identifying the packet number,direction,size,
72 * timestamp,source/destination MAC addresses and packet type.
74 * Thereafter there will be a formated display of the IP and TCP headers as well as a hex string dump
75 * of the headers themselves displayed in the the "IP Header" and "TCP header" fields.
77 * If the packet contains data this is displayed as 4 groups of 16 hex digits followed by an ASCII
78 * representaion of the data line.
80 * Information from the header line, IP header, TCP header and if available data lines are extracted
81 * by the module for displaying.
84 Record Data Record Controller Destination Source Frame
85 Number S/R Length Timer Name MAC Address MAC Address Format
86 ------ --- ------ --------------- ---------- ------------ ------------ ------
87 8 S 145 11:43:59.82956 0006299C14AE 0006299C14FE ETHV2 Type: 0800
88 Frame Type : IP DSCP: 0 ECN: 00-NECT Length: 145 Protocol: TCP Datagram ID: 388B
89 Src Addr: 10.20.144.150 Dest Addr: 10.20.144.151 Fragment Flags: DON'T,LAST
90 IP Header : 45000091388B40004006CC860A1490960A149097
92 TCP . . . : Src Port: 6006,Unassigned Dest Port: 35366,Unassigned
93 SEQ Number: 2666470699 ('9EEF1D2B'X) ACK Number: 2142147535 ('7FAE93CF'X)
94 Code Bits: ACK PSH Window: 32648 TCP Option: NO OP
95 TCP Header : 17768A269EEF1D2B7FAE93CF80187F885B5600000101080A0517E0F805166DE0
96 Data . . . . . : 5443503200020010 0000004980000000 B800000080470103 01001E0000002000 *TCP2.......I*...*...*G........ .*
97 002F010080000004 0300800700C00600 4002008000000304 00800000060FB067 *./..*.....*..*..@..*.....*....*G*
98 FC276228786B3EB0 EF34F5F1D27EF8DF 20926820E7B322AA 739F1FB20D **'B(XK>**4***.** *H **"*S*.*. *
101 /* iSeries unformatted packet record consist of the same header record as the formatted trace but all
102 * other records are simply unformatted data containing IP, TCP and packet data combined.
104 Record Data Record Controller Destination Source Frame Number Number Poll/
105 Number S/R Length Timer Name MAC Address MAC Address Format Command Sent Received Final DSAP SSAP
106 ------ --- ------ --------------- ---------- ------------ ------------ ------ ------- ------ -------- ----- ---- ----
107 1 R 64 12:19:29.97108 000629ECF48E 0006D78E23C2 ETHV2 Type: 0800
108 Data . . . . . : 4500003C27954000 3A06CE3D9797440F 0A5964EAC4F50554 58C9915500000000 *E..<'*@.:.*=**D..YD***.TX**U....*
109 A00216D06A200000 020405B40402080A 1104B6C000000000 010303000B443BF1 **..*J .....*......**.........D;**
115 #include "wtap-int.h"
118 #include "file_wrappers.h"
126 #include <wsutil/str_util.h>
128 #define ISERIES_HDR_MAGIC_STR " COMMUNICATIONS TRACE"
129 #define ISERIES_HDR_MAGIC_LEN 21
130 #define ISERIES_PKT_MAGIC_STR "ETHV2"
131 #define ISERIES_PKT_MAGIC_LEN 5
132 #define ISERIES_LINE_LENGTH 270
133 #define ISERIES_HDR_LINES_TO_CHECK 50
134 #define ISERIES_PKT_LINES_TO_CHECK 4
135 #define ISERIES_MAX_PACKET_LEN 16384
136 #define ISERIES_MAX_TRACE_LEN 99999999
137 #define ISERIES_PKT_ALLOC_SIZE (cap_len*2)+1
138 #define ISERIES_FORMAT_ASCII 1
139 #define ISERIES_FORMAT_UNICODE 2
141 static gboolean iseries_read (wtap * wth, int *err, gchar ** err_info,
142 gint64 *data_offset);
143 static gboolean iseries_seek_read (wtap * wth, gint64 seek_off,
144 union wtap_pseudo_header *pseudo_header,
145 guint8 * pd, int len, int *err,
147 static gboolean iseries_check_file_type (wtap * wth, int *err, int format);
148 static gint64 iseries_seek_next_packet (wtap * wth, int *err);
149 static int iseries_parse_packet (wtap * wth, FILE_T fh,
150 union wtap_pseudo_header *pseudo_header,
151 guint8 * pd, int *err, gchar ** err_info);
152 static int iseries_UNICODE_to_ASCII (guint8 * buf, guint bytes);
153 static gboolean iseries_parse_hex_string (const char * ascii, guint8 * buf,
157 iseries_open (wtap * wth, int *err, gchar ** err_info _U_)
160 char magic[ISERIES_HDR_MAGIC_LEN];
161 /* UNICODE identification */
162 char unicodemagic[ISERIES_HDR_MAGIC_LEN] =
163 { '\xFF', '\xFE', '\x20', '\x00', '\x43', '\x00', '\x4F', '\x00', '\x4D',
164 '\x00', '\x4D', '\x00', '\x55', '\x00', '\x4E', '\x00', '\x49', '\x00',
165 '\x43', '\x00', '\x41'
169 * Check that file starts with a valid iSeries COMMS TRACE header
171 errno = WTAP_ERR_CANT_READ;
172 bytes_read = file_read (&magic, 1, sizeof magic, wth->fh);
173 if (bytes_read != sizeof magic)
175 *err = file_error (wth->fh);
181 /* Check if this is an ASCII formatted file */
182 if (memcmp (magic, ISERIES_HDR_MAGIC_STR, ISERIES_HDR_MAGIC_LEN) == 0)
184 if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
189 * Do some basic sanity checking to ensure we can handle the
190 * contents of this trace
192 if (!iseries_check_file_type (wth, err, ISERIES_FORMAT_ASCII))
199 wth->data_offset = 0;
200 wth->file_encap = WTAP_ENCAP_ETHERNET;
201 wth->file_type = WTAP_FILE_ISERIES;
202 wth->snapshot_length = 0;
203 wth->subtype_read = iseries_read;
204 wth->subtype_seek_read = iseries_seek_read;
205 wth->tsprecision = WTAP_FILE_TSPREC_USEC;
206 if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
213 /* Check if this is a UNICODE formatted file */
214 if (memcmp (magic, unicodemagic, ISERIES_HDR_MAGIC_LEN) == 0)
216 if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
221 * Do some basic sanity checking to ensure we can handle the
222 * contents of this trace
224 if (!iseries_check_file_type (wth, err, ISERIES_FORMAT_UNICODE))
231 wth->data_offset = 0;
232 wth->file_encap = WTAP_ENCAP_ETHERNET;
233 wth->file_type = WTAP_FILE_ISERIES_UNICODE;
234 wth->snapshot_length = 0;
235 wth->subtype_read = iseries_read;
236 wth->subtype_seek_read = iseries_seek_read;
237 wth->tsprecision = WTAP_FILE_TSPREC_USEC;
238 if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
245 /* Neither ASCII or UNICODE so not supported */
250 * Do some basic sanity checking to ensure we can handle the
251 * contents of this trace by checking the header page for
252 * requisit requirements and additional information.
255 iseries_check_file_type (wtap * wth, int *err, int format)
258 int num_items_scanned;
259 char buf[ISERIES_LINE_LENGTH], protocol[9], tcpformat[2];
262 /* Save trace format for passing between packets */
263 sdate = g_malloc (10);
264 wth->capture.iseries = g_malloc (sizeof (iseries_t));
265 wth->capture.iseries->sdate = NULL;
266 wth->capture.iseries->format = format;
267 wth->capture.iseries->tcp_formatted = FALSE;
269 for (line = 0; line < ISERIES_HDR_LINES_TO_CHECK; line++)
271 if (file_gets (buf, ISERIES_LINE_LENGTH, wth->fh) != NULL)
274 * Check that we are dealing with an ETHERNET trace
276 if (wth->capture.iseries->format == ISERIES_FORMAT_UNICODE)
278 iseries_UNICODE_to_ASCII ((guint8 *)buf, ISERIES_LINE_LENGTH);
280 ascii_strup_inplace(buf);
281 num_items_scanned = sscanf (buf,
282 " OBJECT PROTOCOL . . . . . . : %8s",
284 if (num_items_scanned == 1)
286 if (memcmp (protocol, "ETHERNET", 8) != 0)
291 * Determine if the data has been formatted or not
293 num_items_scanned = sscanf (buf,
294 " FORMAT TCP/IP DATA ONLY . . : %1s",
296 if (num_items_scanned == 1)
298 if (strncmp (tcpformat, "Y", 1) == 0)
300 wth->capture.iseries->tcp_formatted = TRUE;
304 wth->capture.iseries->tcp_formatted = FALSE;
309 * The header is the only place where the date part of the timestamp is held, so
310 * extract it here and store for all packets to access
312 num_items_scanned = sscanf (buf,
313 " START DATE/TIME . . . . . . : %8s",
315 if (num_items_scanned == 1)
317 wth->capture.iseries->sdate = sdate;
324 if (file_eof (wth->fh))
327 *err = file_error (wth->fh);
336 * Find the next packet and parse it; called from wtap_read().
339 iseries_read (wtap * wth, int *err, gchar ** err_info, gint64 *data_offset)
345 * Locate the next packet
347 offset = iseries_seek_next_packet (wth, err);
352 * Parse the packet and extract the various fields
355 iseries_parse_packet (wth, wth->fh, &wth->pseudo_header, NULL, err,
360 wth->data_offset = offset;
361 *data_offset = offset;
366 * Seeks to the beginning of the next packet, and returns the
367 * byte offset. Returns -1 on failure, and sets "*err" to the error.
370 iseries_seek_next_packet (wtap * wth, int *err)
372 char buf[ISERIES_LINE_LENGTH];
378 * Seeks to the beginning of the next packet, and returns the
379 * byte offset. Returns -1 on failure, and sets "*err" to the error.
381 for (line = 0; line < ISERIES_MAX_TRACE_LEN; line++)
383 if (file_gets (buf, ISERIES_LINE_LENGTH, wth->fh) != NULL)
386 /* Convert UNICODE to ASCII if required and determine */
387 /* the number of bytes to rewind to beginning of record. */
388 if (wth->capture.iseries->format == ISERIES_FORMAT_UNICODE)
390 /* buflen is #bytes to 1st 0x0A */
391 buflen = iseries_UNICODE_to_ASCII ((guint8 *)buf, ISERIES_LINE_LENGTH);
395 /* Else buflen is just length of the ASCII string */
396 buflen = strlen (buf);
398 /* If packet header found return the offset */
399 if (strncmp (buf + 80, ISERIES_PKT_MAGIC_STR, ISERIES_PKT_MAGIC_LEN)
402 /* Rewind to beginning of line */
403 cur_off = file_tell (wth->fh);
406 *err = file_error (wth->fh);
409 if (file_seek (wth->fh, cur_off - buflen, SEEK_SET, err) == -1)
413 return cur_off - buflen;
416 /* Otherwise we got an error or reached EOF */
419 if (file_eof (wth->fh))
425 /* We (presumably) got an error (there's no equivalent to "ferror()"
426 in zlib, alas, so we don't have a wrapper to check for an error). */
427 *err = file_error (wth->fh);
437 * Read packets in random-access fashion
440 iseries_seek_read (wtap * wth, gint64 seek_off,
441 union wtap_pseudo_header *pseudo_header, guint8 * pd,
442 int len, int *err, gchar ** err_info)
446 /* seek to packet location */
447 if (file_seek (wth->random_fh, seek_off - 1, SEEK_SET, err) == -1)
451 * Parse the packet and extract the various fields
453 pkt_len = iseries_parse_packet (wth, wth->random_fh, pseudo_header, pd,
460 *err = WTAP_ERR_BAD_RECORD;
463 ("iseries: requested length %d doesn't match record length %d",
471 /* Parses a packet. */
473 iseries_parse_packet (wtap * wth, FILE_T fh,
474 union wtap_pseudo_header *pseudo_header, guint8 * pd,
475 int *err, gchar ** err_info)
478 gboolean isValid, isCurrentPacket, IPread, TCPread, isDATA;
479 int num_items_scanned, line, pktline, buflen, i;
481 int cap_len, pktnum, month, day, year, hr, min, sec, csec;
482 char direction[2], destmac[13], srcmac[13], type[5], ipheader[41],
484 char hex1[17], hex2[17], hex3[17], hex4[17];
485 char data[ISERIES_LINE_LENGTH * 2];
487 char *asciibuf, *tcpdatabuf, *workbuf;
491 * Check for packet headers in first 3 lines this should handle page breaks
492 * situations and the header lines output at each page throw and ensure we
493 * read both the captured and packet lengths.
496 for (line = 1; line < ISERIES_PKT_LINES_TO_CHECK; line++)
498 cur_off = file_tell (fh);
499 if (file_gets (data, ISERIES_LINE_LENGTH, fh) == NULL)
501 *err = file_error (fh);
504 *err = WTAP_ERR_SHORT_READ;
508 /* Convert UNICODE data to ASCII */
509 if (wth->capture.iseries->format == ISERIES_FORMAT_UNICODE)
511 iseries_UNICODE_to_ASCII ((guint8 *)data, ISERIES_LINE_LENGTH);
513 /* look for packet header */
514 for (i=0; i<8; i++) {
515 if (strncmp(data+i,"*",1) == 0)
516 g_strlcpy(data+i," ",(ISERIES_LINE_LENGTH * 2));
520 "%6d %1s %6d %d:%d:%d.%d %12s %12s ETHV2 Type: %4s",
521 &pktnum, direction, &cap_len, &hr, &min, &sec, &csec, destmac,
523 if (num_items_scanned == 10)
525 /* OK! We found the packet header line */
528 * XXX - The Capture length returned by the iSeries trace doesn't seem to include the src/dest MAC
529 * addresses or the packet type. So we add them here.
537 * If no packet header found we exit at this point and inform the user.
541 *err = WTAP_ERR_BAD_RECORD;
542 *err_info = g_strdup ("iseries: packet header isn't valid");
547 * If we have Wiretap Header then populate it here
549 * XXX - Timer resolution on the iSeries is hardware dependant; the value for csec may be
550 * different on other platforms though all the traces I've seen seem to show resolution
551 * to Milliseconds (i.e HH:MM:SS.nnnnn) or Nanoseconds (i.e HH:MM:SS.nnnnnn)
553 if (wth->capture.iseries->sdate)
556 sscanf (wth->capture.iseries->sdate, "%d/%d/%d", &month, &day, &year);
557 tm.tm_year = 100 + year;
558 tm.tm_mon = month - 1;
564 wth->phdr.ts.secs = mktime (&tm);
565 /* Handle Millisecond precision for timer */
568 wth->phdr.ts.nsecs = csec * 1000;
570 /* Handle Nanosecond precision for timer */
573 wth->phdr.ts.nsecs = csec * 10000;
577 wth->phdr.caplen = cap_len;
578 wth->phdr.pkt_encap = WTAP_ENCAP_ETHERNET;
579 pseudo_header->eth.fcs_len = -1;
582 * Start Reading packet contents
584 isCurrentPacket = TRUE;
589 * Allocate 2 work buffers to handle concatentation of the hex data block
591 tcpdatabuf = g_malloc (ISERIES_PKT_ALLOC_SIZE);
592 g_snprintf (tcpdatabuf, 1, "%s", "");
593 workbuf = g_malloc (ISERIES_PKT_ALLOC_SIZE);
594 g_snprintf (workbuf, 1, "%s", "");
595 /* loop through packet lines and breakout when the next packet header is read */
597 while (isCurrentPacket)
600 /* Read the next line */
601 if (file_gets (data, ISERIES_LINE_LENGTH, fh) == NULL)
609 *err = file_error (fh);
612 *err = WTAP_ERR_SHORT_READ;
618 /* Convert UNICODE data to ASCII and determine line length */
619 if (wth->capture.iseries->format == ISERIES_FORMAT_UNICODE)
621 buflen = iseries_UNICODE_to_ASCII ((guint8 *)data, ISERIES_LINE_LENGTH);
625 /* Else bytes to rewind is just length of ASCII string */
626 buflen = strlen (data);
629 /* If this is a IP header hex string then set flag */
630 num_items_scanned = sscanf (data + 22, "IP Header : %40s", ipheader);
631 if (num_items_scanned == 1)
636 /* If this is TCP header hex string then set flag */
637 num_items_scanned = sscanf (data + 22, "TCP Header : %80s", tcpheader);
638 if (num_items_scanned == 1)
644 * If there is data in the packet handle it here.
646 * The data header line will have the "Data . . " identifier, subsequent lines don't
649 sscanf (data + 27, "%16[A-Z0-9] %16[A-Z0-9] %16[A-Z0-9] %16[A-Z0-9]",
650 hex1, hex2, hex3, hex4);
651 if (num_items_scanned > 0)
655 * Scan the data line for data blocks, depending on the number of blocks scanned
656 * add them along with current tcpdata buffer to the work buffer and then copy
657 * work buffer to tcpdata buffer to continue building up tcpdata buffer to contain
658 * a single hex string.
660 switch (num_items_scanned)
663 g_snprintf (workbuf, ISERIES_PKT_ALLOC_SIZE, "%s%s", tcpdatabuf,
667 g_snprintf (workbuf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s",
668 tcpdatabuf, hex1, hex2);
671 g_snprintf (workbuf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s%s",
672 tcpdatabuf, hex1, hex2, hex3);
675 g_snprintf (workbuf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s%s%s",
676 tcpdatabuf, hex1, hex2, hex3, hex4);
678 memcpy (tcpdatabuf, workbuf, ISERIES_PKT_ALLOC_SIZE);
682 * If we see the identifier for the next packet then rewind and set
683 * isCurrentPacket FALSE
685 if ((strncmp (data + 80, ISERIES_PKT_MAGIC_STR, ISERIES_PKT_MAGIC_LEN)
686 == 0) && pktline > 1)
688 isCurrentPacket = FALSE;
689 cur_off = file_tell (fh);
693 *err = file_error (fh);
696 if (file_seek (fh, cur_off - buflen, SEEK_SET, err) == -1)
704 * For a formated trace ensure we have read at least the IP and TCP headers otherwise
705 * exit and pass error message to user.
707 if (wth->capture.iseries->tcp_formatted)
711 *err = WTAP_ERR_BAD_RECORD;
712 *err_info = g_strdup ("iseries: IP header isn't valid");
717 *err = WTAP_ERR_BAD_RECORD;
718 *err_info = g_strdup ("iseries: TCP header isn't valid");
724 * Create a buffer to hold all the ASCII Hex data and populate with all the
727 asciibuf = g_malloc (ISERIES_PKT_ALLOC_SIZE);
730 /* packet contained data */
731 if (wth->capture.iseries->tcp_formatted)
733 /* build string for formatted fields */
734 g_snprintf (asciibuf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s%s%s%s",
735 destmac, srcmac, type, ipheader, tcpheader, tcpdatabuf);
739 /* build string for unformatted data fields */
740 g_snprintf (asciibuf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s%s", destmac,
741 srcmac, type, tcpdatabuf);
746 /* No data in the packet */
747 g_snprintf (asciibuf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s%s%s", destmac,
748 srcmac, type, ipheader, tcpheader);
752 * Extract the packet length from the actual IP header; this may
753 * differ from the capture length reported by the formatted trace.
754 * Note: if the entire Ethernet packet is present, but the IP
755 * packet is less than 46 bytes long, there will be padding, and
756 * the length in the IP header won't include the padding; if
757 * the packet length is less than the captured length, set the
758 * packet length to the captured length.
760 num_items_scanned = sscanf (asciibuf + 32, "%4x", &pkt_len);
761 wth->phdr.len = pkt_len + 14;
762 if (wth->phdr.caplen > wth->phdr.len)
763 wth->phdr.len = wth->phdr.caplen;
765 /* Make sure we have enough room for the packet, only create buffer if none supplied */
768 buffer_assure_space (wth->frame_buffer, ISERIES_MAX_PACKET_LEN);
769 buf = buffer_start_ptr (wth->frame_buffer);
770 /* Convert ascii data to binary and return in the frame buffer */
771 iseries_parse_hex_string (asciibuf, buf, strlen (asciibuf));
775 /* Convert ascii data to binary and return in the frame buffer */
776 iseries_parse_hex_string (asciibuf, pd, strlen (asciibuf));
779 /* free buffers allocs and return */
784 return wth->phdr.len;
788 * Simple routine to convert an UNICODE buffer to ASCII
790 * XXX - This may be possible with iconv or similar
793 iseries_UNICODE_to_ASCII (guint8 * buf, guint bytes)
799 for (i = 0; i < bytes; i++)
818 * Simple routine to convert an ASCII hex string to binary data
819 * Requires ASCII hex data and buffer to populate with binary data
822 iseries_parse_hex_string (const char * ascii, guint8 * buf, int len)
834 hexvalue = g_ascii_xdigit_value(ascii[i]);
837 return FALSE; /* not a valid hex digit */
838 bytevalue = (guint8)(hexvalue << 4);
840 return FALSE; /* only one hex digit of the byte is present */
841 hexvalue = g_ascii_xdigit_value(ascii[i]);
844 return FALSE; /* not a valid hex digit */
845 bytevalue |= (guint8) hexvalue;
846 buf[byte] = bytevalue;