# Convert packet_monitor output to a pcap file

The packet_monitor tool is the packet capture and display tool that is part of the STCP product set for VOS. It displays packets in a text format and while it will decode the Ethernet, ARP, IP and TCP/UDP/ICMP headers it displays the upper layer protocols as hex data. It has some filtering capability but only to control which packets to capture. Following a specific TCP connection or tracing sequence numbers to locate retransmissions or missing segments has to be done manually.

The following perl script pm2text2pcap.pl is my attempt to convert the packet_monitor output to something that the wireshark text2pcap tool can read. Once the output is converted into a pcap format it can be read by any number of protocol analyzers - including but not limited to wireshark.

In order for this tool to correctly parse the packet_monitor output, packet_monitor must be run with the following control arguments:
-numeric -time_stamp -verbose -pkt_hdr -hex_header -hex_dump -length 1500
Stricly speaking the "-length 1500" is not needed but without it you may end up with only part of the upper protocol. You can also add any capture filters you want. Version 1.4 (or later) of the pm command macro can be used to generate the output. In addition the input file should end with at least 1 non packet line. If you use pm it will typically be ready and process termination messages, but any line will work. If you do not have such a line you may get the error:
Use of uninitialized value in length at pm2text2pcap.pl line 124, <stdin> line xx.
Where xx is the last line of the input file. The error does no harm and may be ignored.

The perl script uses IO redirection to control both the input and output. The following command runs the script, taking as the input file pm-test-15.out and generating the file pm-test-15.txt.

  C:\>perl pm2text2pcap.pl < pm-test-15.out > pm-test-15.txt VOS RELEASE not set. Sequence numbers and window size treated as decimal values. To set a VOS release use the release number as an argument, i.e. pm2text2pcap 14.7 < input_file > output_file You need to set a release ONLY if the packet_monitor file is taken from a release 14 or earlier system. 

Note the message about VOS release not being set and how to set it. The output of packet_monitor changed in VOS release 15. Prior to that the sequence and ACK numbers and the window size were displayed as hex values. Starting in release 15 they are displayed as decimal values. The script needs to know the VOS release inorder to know how to handle the numbers. As the message says you can provide a release number as an argument after the script name.

The following are examples of setting the release to 15.3 and 14.7. Note that a message indicates if the numbers will be treated as hex or decimal.

  C:\>perl pm2text2pcap.pl 15.3 < pm-test-15.out > pm-test-15.txt VOS RELEASE set to 15.3. Sequence numbers and window size treated as decimal val ues C:\>perl pm2text2pcap.pl 14.7 < pm-test-15.out > pm-test-15.txt VOS RELEASE set to 14.7. Sequence numbers and window size treated as hex values 

Specifing a release 14 for a packet_monitor output that was created on a release 15 system will not generate any errors but will produce incorrect sequence and window size numbers since the decimal value will be treated as a hexidecimal value. Specifing a release 15 (or greater) for a packet_monitor output that was created on a release 14 system will generate errors for every value that is not a decimal value. An output file will still be created by the numbers will be 0.

  C:\>perl pm2text 2pcap.pl 16.2 < pm-test-14.out > pm-test-14.txt VOS RELEASE set to 16.2. Sequence numbers and window size treated as decimal val ues Argument "39f61f50" isn't numeric in sprintf at pm2text2pcap.pl line 269, < stdin > line 12. Argument "42b614c8" isn't numeric in sprintf at pm2text2pcap.pl line 279, < stdin > line 12. Argument "39f61f52" isn't numeric in sprintf at pm2text2pcap.pl line 269, < stdin > line 24. 

When complete the output will look something like:

  16:39:03.706 000000 ff ff ff ff ff ff 00 0c 6e 3a ee 63 08 06 00 01 ................ 000010 08 00 06 04 00 01 00 0c 6e 3a ee 63 a4 98 4d a0 ................ 000020 00 00 00 00 00 00 a4 98 4d 75 ................ 16:39:04.989 000000 ff ff ff ff ff ff 00 90 27 c7 93 06 08 06 00 01 ................ 000010 08 00 06 04 00 01 00 90 27 c7 93 06 a4 98 4d f8 ................ 000020 00 00 00 00 00 00 a4 98 4c 49 ................ 16:39:11.787 000000 ff ff ff ff ff ff 00 18 8b 43 39 0c 08 06 00 01 ................ 000010 08 00 06 04 00 01 00 18 8b 43 39 0c a4 98 4c 25 ................ 000020 00 00 00 00 00 00 a4 98 4d 6f ................ 16:39:12.416 000000 ff ff ff ff ff ff 00 d0 b7 9e d0 39 08 06 00 01 ................ 000010 08 00 06 04 00 01 00 d0 b7 9e d0 39 a4 98 4d f9 ................ 000020 00 00 00 00 00 00 a4 98 4d e0 ................ 

You then need to run the text2pcap utility with the
C:\temp>"C:\program files\wireshark\text2pcap" -t "%H:%M:%S." -q foo.out foo.pcap
The -t argument tells text2pcap to interpret the line preceeding the frame as a timestamp. Don't forget the colons (:) between the %H, %M and %S and the dot following the "S". The dot indicates that the numbers following the seconds should be interpreted as a partial second. The -q tells text2pcap to be quiet. Without it, a line is output for every frame processed. The first file name is the input file and the second is the output file.

Once you have the output file it can be processed by any analyzer that will read a pcap file - and most of them will.

One final note about the script. This is version 1.00 - I've done my best to test it but packet monitor can output data in multiple formats and I may have missed something. The script tries to take care of this by confirming that the output characters for the frame are all in the range 0-9 or a-f. If one of the output characters is not in this range it will NOT output the frame. Instead it will out a frame with 0 for the source and destination Ethernet addresses, an Ethertype of 0xffff and bytes of "The original frame was not able to be parsed". It looks like this in the output file:

  000000 00 00 00 00 00 00 00 00 00 00 00 00 ff ff 54 68 ................ 000010 65 20 6f 72 69 67 69 6e 61 6c 20 66 72 61 6d 65 ................ 000020 20 77 61 73 20 6e 6f 74 20 61 62 6c 65 20 74 6f ................ 000030 20 62 65 20 70 61 72 73 65 64 ................ 

and in wireshark the frame will look like:

Figure 1 - Parse Error Frame

If there are errors reported please let me know and forward the packet_monitor trace to me for testing. Note however, that if packet_monitor is run without all of the required arguments you will also get parse errors.

Finally, the script will add a special frame to the end of the trace. It states that the trace was originally created by packet_monitor, identifies the version of the script and gives the date that the script was run. There is a corresponding comment as well. It looks like this in the output file:

  14:52:58.662 000000 00 00 00 00 00 00 00 00 00 00 00 00 ff ff 54 68 ................ 000010 69 73 20 74 72 61 63 65 20 6f 72 69 67 69 6e 61 ................ 000020 6c 6c 79 20 63 61 70 74 75 72 65 64 20 62 79 20 ................ 000030 70 61 63 6b 65 74 5f 6d 6f 6e 69 74 6f 72 20 61 ................ 000040 6e 64 20 63 6f 6e 76 65 72 74 65 64 20 69 6e 74 ................ 000050 6f 20 61 20 66 69 6c 65 20 73 75 69 74 61 62 6c ................ 000060 65 20 66 6f 72 20 70 72 6f 63 65 73 73 69 6e 67 ................ 000070 20 69 6e 74 6f 20 61 20 70 63 61 70 20 66 69 6c ................ 000080 65 20 62 79 20 70 6d 32 74 65 78 74 32 70 63 61 ................ 000090 70 2e 70 6c 20 76 65 72 73 69 6f 6e 20 31 2e 30 ................ 0000a0 30 20 6f 6e 20 57 65 64 20 4d 61 72 20 32 36 20 ................ 0000b0 32 30 3a 30 31 3a 32 35 20 32 30 30 38 ................ #This trace originally captured by packet_monitor and converted into a file suitable for processing into a pcap file by pm2text2pcap.pl version 1.00 on Wed Mar 26 20:01:25 2008 

and in wireshark the frame will look like:

Figure 2 - Final Frame

## pm2text2pcap.pl

  # pm2text2pcap begins here # # Version 0.00 07-10-31 # Version 1.00 08-03-26 added code to process an argument identifing the VOS release # before release 15 packet monitor displayed sequence and acknowledgement # numbers and the window size in hex. Starting in release 15 the numbers are # displayed in decimal. The original version always treated these numbers as # decimal. If no argument is given the numbers are treated as decimal. # Version 1.10 10-11-26 Added disclaimer # noah.davids@stratus.com # # See noahdavids.org/self_published/pm2text2pcap.html for documentation # # This software is provided on an "AS IS" basis, WITHOUT ANY WARRANTY OR ANY # SUPPORT OF ANY KIND. The AUTHOR SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES # OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE. This disclaimer # applies, despite any verbal representations of any kind provided by the # author or anyone else. # use strict; use warnings; my ($VOS_RELEASE,$treatAsHex); my ($ft, @f,$limit); my ($ulp); my ($sp, $spx,$dp, $dpx,$cksum1, $cksum2,$length, $lengthx); my ($numbytes, $byte,$x); my ($seq,$seqx, $ack,$ackx, $window,$windowx, $len,$flags); my ($xoff,$urg1, $urg2); my ($tip1, $tip2,$tip3, $tip4); my ($sip1, $sip2,$sip3, $sip4); my ($now, $message,$c); sub printFrame { my ($ft,$limit, @f) = @_; my ($y,$spaces, $x); my ($OKToPrint); $OKToPrint = 1; for ($y = 0; $y <$limit; $y++) {$f [$y] =~ tr/ /0/; if (!($f [$y] =~ (m/([0-9a-f][0-9a-f])/x))) {$OKToPrint = 0; last; } } if ($OKToPrint) { print "\n",$ft; for ($y = 0;$y <= $limit;$y++) { if ($y % 16 == 0) { if ($y == 0) { printf ("\n%06x", $y); } else { printf ("\t................\n%06x",$y); } } $f [$y] =~ tr/ /0/; print " ", $f [$y]; $f [$y] = ""; } $spaces = 55 - (6 + (($y % 16) * 3)); for ($x = 1;$x < $spaces;$x++) { print " "; } printf ("\t................\n"); } else { $f [0] =$f [1] = $f [2] =$f [3] = $f [4] =$f [5] = $f [6] =$f [7] = $f [8] =$f [9] = $f [10] =$f [11] = "00"; $f [12] =$f [13] = "ff"; $limit = 13;$message = "The original frame was not able to be parsed"; for ($x = 0;$x < length ($message);$x++) { $c = substr ($message, $x, 1);$f [++$limit] = sprintf ("%2x", ord ($c)); } printFrame ($ft,$limit, @f); } } $VOS_RELEASE =$ARGV [0]; if (defined $VOS_RELEASE) { if ($VOS_RELEASE < 15) { print STDERR "VOS RELEASE set to " . $VOS_RELEASE . ". Sequence numbers and window size treated as hex values \n";$treatAsHex = 1; } else { print STDERR "VOS RELEASE set to " . $VOS_RELEASE . ". Sequence numbers and window size treated as decimal values \n";$treatAsHex = 0; } } else { print STDERR "VOS RELEASE not set" . ". Sequence numbers and window size treated as decimal values. \n"; print STDERR "To set a VOS release use the release number as an argument, i.e. \n"; print STDERR " pm2text2pcap 14.7 < input_file > output_file \n"; print STDERR "You need to set a release ONLY if the packet_monitor file is taken \n"; print STDERR "from a release 14 or earlier system. \n\n\n"; $treatAsHex = 0; } while ($_ = ) { if (/Ether Dst/) { ($ft,$f [0], $f [1],$f [2], $f [3],$f [4], $f [5],$f [6], $f [7],$f [8], $f [9],$f [10], $f [11],$f [12], $f [13]) = (m/^(............) .* Dst \s* (..) : (..) : (..) : (..) : (..) : (..) \s* Src \s* (..) : (..) : (..) : (..) : (..) : (..) \s* Type \s* (..) (..)/x); if (($f [13] eq "00") && (length $f [13] == 2)) {$_ = ; $_ = ; ($f [14], $f [15],$f [16], $f [17],$f [18], $f [19],$f [20], $f [21],$f [22], $f [23],$f [24], $f [25],$f [26], $f [27],$f [28], $f [29]) = (m/^........... (..) . (..) . (..) . (..) . (..) . (..) . (..) . (..) .. (..) . (..) . (..) . (..) . (..) . (..) . (..) . (..)/x);$_ = ; ($f [30],$f [31], $f [32],$f [33], $f [34],$f [35], $f [36],$f [37], $f [38],$f [39], $f [40],$f [41], $f [42],$f [43], $f [44],$f [45]) = (m/^........... (..) . (..) . (..) . (..) . (..) . (..) . (..) . (..) .. (..) . (..) . (..) . (..) . (..) . (..) . (..) . (..)/x); for ($x = 20;$x < 45; $x++) { if ($f [$x] ne " ") {$limit = $x; } }$_ = ; # skip line one of interpreted IP header $_ = ; # skip line two of interpreted IP header$_ = ; ($ulp) = (m/^(...)/x); if ($ulp eq "UDP") { ($sp,$dp, $cksum1,$cksum2, $length) = (m/.* from \s* \d* . \d* . \d* . \d* . (\w*) .* to \s \d* . \d* . \d* . \d* . (\w*) \s* Cksum \s* (..)(..) , \s* (\d*)/x); if ($sp eq "tftp") {$sp = "69"} if ($dp eq "tftp") {$dp = "69"} if ($sp eq "time") {$sp = "37"} if ($dp eq "time") {$dp = "37"} if ($sp eq "name") {$sp = "42"} if ($dp eq "name") {$dp = "42"} if ($sp eq "domain") {$sp = "53"} if ($dp eq "domain") {$dp = "53"} if ($sp eq "biff") {$sp = "512"} if ($dp eq "biff") {$dp = "512"} if ($sp eq "talk") {$sp = "517"} if ($dp eq "talk") {$dp = "517"} if ($sp eq "route") {$sp = "520"} if ($dp eq "route") {$dp = "520"}$spx = sprintf ("%04x", $sp); ($f [++$limit],$f [++$limit]) = ($spx =~ (m/(..) (..)/x)); $dpx = sprintf ("%04x",$dp); ($f [++$limit], $f [++$limit]) = ($dpx =~ (m/(..) (..)/x));$lengthx = sprintf ("%04x", $length); ($f [++$limit],$f [++$limit]) = ($lengthx =~ (m/(..) (..)/x)); $f [++$limit] = $cksum1;$f [++$limit] =$cksum2; $_ = ; # offset header line$_ = ; while (length ($_) > 1) { if (/contains/) { ($numbytes, $byte) = (m/^.* contains \s* (\d*) .* bytes \s* of \s* 0x (..) /x); for ($x = 1; $x <=$numbytes; $x++) {$f [++$limit] =$byte; } } else { $_ = substr ($_, 0, -1) . " \n"; ($f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit]) = (m/^........... (..) . (..) . (..) . (..) . (..) . (..) . (..) . (..) .. (..) . (..) . (..) . (..) . (..) . (..) . (..) . (..)/x); } $_ = ; } printFrame ($ft, $limit, @f); } if ($ulp eq "ICM") { $_ = ; # offset header line$_ = ; # first line of data; while (length ($_) > 1) { if (/contains/) { ($numbytes, $byte) = (m/^.* contains \s* (\d*) .* bytes \s* of \s* 0x (..) /x); for ($x = 1; $x <=$numbytes; $x++) {$f [++$limit] =$byte; } } else { $_ = substr ($_, 0, -1) . " \n"; ($f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit]) = (m/^........... (..) . (..) . (..) . (..) . (..) . (..) . (..) . (..) .. (..) . (..) . (..) . (..) . (..) . (..) . (..) . (..)/x); } $_ = ; } printFrame ($ft, $limit, @f); } if ($ulp eq "TCP") { ($sp,$dp) = (m/.* from \s* \d* . \d* . \d* . \d* . (\w*) .* to \s \d* . \d* . \d* . \d* . (\w*)/x); if ($sp eq "telnet") {$sp = "23"} if ($dp eq "telnet") {$dp = "23"} if ($sp eq "ftp") {$sp = "21"} if ($dp eq "ftp") {$dp = "21"} if ($sp eq "ftp-data") {$sp = "20"} if ($dp eq "ftp-data") {$dp = "20"} if ($sp eq "smtp") {$sp = "25"} if ($dp eq "smtp") {$dp = "25"} if ($sp eq "whois") {$sp = "43"} if ($dp eq "whois") {$dp = "43"} if ($sp eq "domain") {$sp = "53"} if ($dp eq "domain") {$dp = "53"} if ($sp eq "finger") {$sp = "79"} if ($dp eq "finger") {$dp = "79"} if ($sp eq "sunrpc") {$sp = "111"} if ($dp eq "sunrpc") {$dp = "111"} if ($sp eq "X400") {$sp = "103"} if ($dp eq "X400") {$dp = "103"} if ($sp eq "X400-snd ") {$sp = "104"} if ($dp eq "X400-snd ") {$dp = "104"} if ($sp eq "pop-2") {$sp = "109"} if ($dp eq "pop-2") {$dp = "109"} if ($sp eq "uucp-path") {$sp = "117"} if ($dp eq "uucp-path") {$dp = "117"} if ($sp eq "nntp") {$sp = "119"} if ($dp eq "nntp") {$dp = "119"} if ($sp eq "ntp") {$sp = "123"} if ($dp eq "ntp") {$dp = "123"} if ($sp eq "ingreslock") {$sp = "1524"} if ($dp eq "ingreslock") {$dp = "1524"} $spx = sprintf ("%04x",$sp); ($f [++$limit], $f [++$limit]) = ($spx =~ (m/(..) (..)/x));$dpx = sprintf ("%04x", $dp); ($f [++$limit],$f [++$limit]) = ($dpx =~ (m/(..) (..)/x)); $_ = ;$_ =~ s/n.a./0/g; # ack and windows may be n.a. which stands for 0 ($seq,$ack, $window,$len, $flags) = (m/^.* seq \s* (\w*) .* ack \s* (\w*) .* window \s* (\w*) , \s* (\d*) .* flags (.*$)/x); if ($treatAsHex) {$seqx = sprintf ("%s%s", substr ("00000000", 1, (8 - length ($seq))),$seq); } else { $seqx = sprintf ("%08x",$seq); } ($f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit]) = ($seqx =~ (m/(..) (..) (..) (..)/x)); if ($treatAsHex) { $ackx = sprintf ("%s%s", substr ("00000000", 1, (8 - length ($ack))), $ack); } else {$ackx = sprintf ("%08x", $ack); } ($f [++$limit],$f [++$limit],$f [++$limit],$f [++$limit]) = ($ackx =~ (m/(..) (..) (..) (..)/x)); $_ = ; ($xoff, $flags,$cksum1, $cksum2,$urg1, $urg2) = (m/^.* X.Off \s* 0 (.) .* Flags \s* (..) .* Cksum \s* (..) (..) .* Urg-> \s* (..) (..)/x);$f [++$limit] =$xoff . "0"; $f [++$limit] = $flags; if ($treatAsHex) { $windowx = sprintf ("%s%s", substr ("00000000", 1, (4 - length ($window))), $window); } else {$windowx = sprintf ("%04x", $window); } ($f [++$limit],$f [++$limit]) = ($windowx =~ (m/(..) (..)/x)); $f [++$limit] = $cksum1;$f [++$limit] =$cksum2; $f [++$limit] = $urg1;$f [++$limit] =$urg2; $_ = ; # offset header line or the no TCP data line$_ = ; # either the first line of data or blank while (length ($_) > 1) { if (/contains/) { ($numbytes, $byte) = (m/^.* contains \s* (\d*) .* bytes \s* of \s* 0x (..) /x); for ($x = 1; $x <=$numbytes; $x++) {$f [++$limit] =$byte; } } else { $_ = substr ($_, 0, -1) . " \n"; ($f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit], $f [++$limit]) = (m/^........... (..) . (..) . (..) . (..) . (..) . (..) . (..) . (..) .. (..) . (..) . (..) . (..) . (..) . (..) . (..) . (..)/x); } $_ = ; } printFrame ($ft, $limit, @f); } } if ($f [13] eq "06") { $f [14] = "00";$f [15] = "01"; $f [16] = "08";$f [17] = "00"; $f [18] = "06";$f [19] = "04"; $_ = ; if (/ARP Req/) {$f [20] = "00"; $f [21] = "01"; ($tip1, $tip2,$tip3, $tip4) = (m/^.* Target \s* (\d*) . (\d*) . (\d*) . (\d*)/x); ($sip1, $sip2,$sip3, $sip4,$f [22], $f [23],$f [24], $f [25],$f [26], $f [27]) = (m/^.* Src \s* (\d*) . (\d*) . (\d*) . (\d*) \s* \[ (..) : (..) : (..) : (..) : (..) : (..)/x);$f [28] = sprintf ("%02x", $sip1);$f [29] = sprintf ("%02x", $sip2);$f [30] = sprintf ("%02x", $sip3);$f [31] = sprintf ("%02x", $sip4);$f [32] = $f [33] =$f [34] = $f [35] =$f [36] = $f [37] = "00";$f [38] = sprintf ("%02x", $tip1);$f [39] = sprintf ("%02x", $tip2);$f [40] = sprintf ("%02x", $tip3);$f [41] = sprintf ("%02x", $tip4); } if (/ARP Rep/) {$f [20] = "00"; $f [21] = "02"; ($tip1, $tip2,$tip3, $tip4,$f [32], $f [33],$f [34], $f [35],$f [36], $f [37]) = (m/^.* Target \s* (\d*) . (\d*) . (\d*) . (\d*) \s* \[ (..) : (..) : (..) : (..) : (..) : (..)/x); ($sip1, $sip2,$sip3, $sip4,$f [22], $f [23],$f [24], $f [25],$f [26], $f [27]) = (m/^.* Src \s* (\d*) . (\d*) . (\d*) . (\d*) \s* \[ (..) : (..) : (..) : (..) : (..) : (..)/x);$f [28] = sprintf ("%02x", $sip1);$f [29] = sprintf ("%02x", $sip2);$f [30] = sprintf ("%02x", $sip3);$f [31] = sprintf ("%02x", $sip4);$f [38] = sprintf ("%02x", $tip1);$f [39] = sprintf ("%02x", $tip2);$f [40] = sprintf ("%02x", $tip3);$f [41] = sprintf ("%02x", $tip4); }$limit = 41; printFrame ($ft,$limit, @f); } $f [13] = ""; } }$f [0] = $f [1] =$f [2] = $f [3] =$f [4] = $f [5] =$f [6] = $f [7] =$f [8] = $f [9] =$f [10] = $f [11] = "00";$f [12] = $f [13] = "ff";$limit = 13; $now = localtime;$message = "This trace originally captured by packet_monitor and " . "converted into a file suitable for processing into a pcap " . "file by pm2text2pcap.pl version 1.00 on " . $now; for ($x = 0; $x < length ($message); $x++) {$c = substr ($message,$x, 1); $f [++$limit] = sprintf ("%2x", ord ($c)); } printFrame ($ft, $limit, @f); print "\n#",$message, "\n"; # # pm2text2pcap ends here