This article was the one I posted to another site way back in late 1990 when I was at Ascend Communications. I just found this article at an archive site today. As I don’t know how long this archive site continues its service, I decided to restore it on my personal site. Another reason for doing this was that I wanted to express my great appreciation to Yuzo Yamashita, one of my ex-colleagues at Ascend Communications who contributed a wrapper for my H.323 decoder and I would like to offer my sincere condolences for his loss in 2012.

h323decoder Primer

Motonori Shindo (mshindo@ascned.co.jp)

What is h323decoder?

The best way to understand how H.323 protocol works would be to examine the actual packets between a gatekeeper, gateways, and/or terminals. H.323-related protocols such as H.225.0 and H.245 use PER (Packed Encoding Rule) for encoding packets. While this encoding scheme generates very compact message, it’s very hard to decode for human (I tried it once and finally gave it up:-)). h323decoder is a tool to decode H.323 packets and show you the result in more human-friendly way. This tool will alleviate your pain to decode H.323 packets by hand and hopefully help you understand the protocols themselves better.

Generally speaking, making an H.323 decoder is NOT a trivial task. It basically requires an ASN.1 compiler and a PER decoder. Fortunately there’s an ambitious project called ‘OpenH323’ and this project has an open-source ASN.1 compiler and PER encoder/decoder. I made use of OpenH323’s code and wrote a simple H.323 decoder. I could not have been able to write this h323decoder without OpenH323 Project. I would like to say thank you all who contributed to this project!

How do I get the h323decoder?

You probably don’t need to get an h323decoder. There’s a CGI version of this decoder, so all you have to have is a browser (e.g. Netscape, Internet Explorer, etc.). [Sorry, this is under construction now.]

For those who want to have this program as a standalone program so that you can run it on your UNIX machine, please get this. This is a patch to openH323 code (0.8-alpha1). Please consult README file under h323decoder directory for more detail. To compile openH323 source code, you need to have a corresponding PWLib library. For the latest openH323 source code and PWLib library, please obtain them from here. To compile pwlib_min_1.10 under FreeBSD, it requres a patch which you can download from here. h323decoder may also compile and work under Windows, but I haven’t tried it myself. If someone succeeded, please let me know.

How do I use the h323decoder?

h323decoder takes a hex dump of the payload you want to decode as an input. For example, hex dump that is generated by the command ‘tcpdump -x’ can be an input, but other forms of a hex dump would also be accepted.

Honestly, h323decoder is not sophisticated at all, especially in terms of a human interface. Therefore, users of this decoder must be intelligent on behalf it:-) For example, since this decoder takes only H.323 payload and don’t strip any IP, TCP/UDP, TPKT header, you have to delineate these headers and strip them accordingly by yourself.

The decoder can handle three types of H.323 messages:

  • RAS/H.225.0 message

  • Q.931/H.225.0 message

  • H.245 Media System Control message

The decoder doesn’t automatically detect which type of message you’re trying to decode. Instead, you must specify it explicitly.

Once you extracted the payload that h323decoder can process and determined the type of a message you want to decode, the rest of the job is easy. If you’re using the CGI version of this decoder via a browser, select the type of the message from a pulldown menu, and enter the hex dump into the text area, then click the “submit” button. If you’re using a standalone version of h323decoder, you must specify the type of the message by the command line argument (‘ras’, ‘q931’, or ‘msc’), and then pass an hex dump to the decoder through the standard input. Let’s suppose you have an hex dump of RAS message in hexdump.txt file, then

% cat hexdump.txt | h323decoder ras

will print out the result to the standard output.

Please note that current h323decoder can handle only one message at a time. You can not let it decode multiple messages in a sequence. There’s a perl script that can be used with h323decoder. Please refer to the end part of this document.

How do I extract an H.323 payload?

As I said before, determine the type of message you are about to decode. The first message you will come across might be a RAS/H.225.0 message.

Since RAS/H.225.0 uses UDP and port number 1719, you can easily find RAS/H.2250.0 messages in the packet trace. Knowing that IP header is (usually) 20 bytes long, and UDP header is 8 bytes long, it’s easy to extract a RAS message out of the IP packet. If you had a bad luck, it could be fragmented. In that case, you’ll have to reassemble them accordingly.

In case of Q.931/H.225.0 message, things get more complicated. Q.931/H.225.0 is a TCP message and usually uses a port number 1720, so finding a Q.931/H.225 message in the packet trace is relatively easy. Since TCP provides us a “stream” of packet and there is no logical boundaries in the stream, there must be a way to delineate the Q.931/H.225.0 messages on top of TCP. TPKT header is used for this purpose. Q.931/H.225.0 message is wrapped by TPKT header, which consists of 2 octets (‘0003’) and another 2 octets that represents the total length of the message (including the length of TPKT header itself).

Following to the TPKT header, you’ll see some Q.931 Information Elements (IEs). While this part contains some important information, H.323-specific data is stored as a Q.931’s ‘User-User Information Element’. This IE starts with ‘0x7e’ followed by 2-octet length field and finally a protocol discriminator which is always ‘0x05’. Forgetting all these details of Q.931 packet format, there’s a rule of thumb that will work most of the time; “Find ‘0x7e’ and get rid of the next 3 octets. The rest is (most likely) a Q.931/H.225.0 payload that can be fed into the h323decoder.”

In case of H.245 message, it’s hard to tell if it’s an H.245 message or not by just looking at the packet, because it doesn’t use a fixed port number. To know what port number is used for H.245 message, you need to examine the content of the previous Q.931/H225.0 messages exhcanged between the gateways and/or the terminals. But, practically, you may assume that the packet is H.245 if it’s not a RAS/H.225.0 nor a Q.931/H.225.0 message. H.245 uses TCP as the transport and encapsulate the packet using TPKT header in the same way as in Q.931/H.225.0 as I described before.

Since both UDP (RAS/H.225.0) and TCP (Q.931/H.225.0 and H.245) run on IP, you must take the possibility of a fragmentation and an out-of-order delivery of the packet into account when you try to decode the H.323 packets. In addition to that, please be aware that TCP doesn’t guarantee that a PDU (Q.931/H.225.0 or H.245 message) is put onto the network as a single packet (i.e. it can be chopped), therefore you may need to concatenate them until they makes sense. Similary, there may be a case where two PDUs are sent to the network as a single TCP packet (in this case, you’ll have to chop it into two or more H.323 PDUs by hand). Furthermore, there may be chance that TCP timeout occurs and a retransmission takes place. In this case, you have to throw away the duplicated packets, again, by hand.

Though the task I described above may look too hard, it isn’t that bad as you might imagine. If you get used to it, you just glance at the hex dump and can extract the H.323 payload relatively easily! Please give it a shot!

Could I have some examples?

Sure! Here’s a packet snooped by tcpdump. The following examples are all taken using Lucent’s (formally Ascend) MultiVoice system.

max2000.ascend.co.jp.1719 > navis.ascend.co.jp.1719: udp 80
<span style="color: #339966;">4500 006c 6a02 0000 4011 f0ca c0a8 8911
caf6 0b04</span> <span style="color: #800080;">06b7 06b7 0058 0000</span> <span style="color: #ff0000;">2590 ffe2
0860 006d 0061 0078 0032 0030 0030 0030
0201 0044 a010 44a0 0240 0600 6d00 6100
7800 3200 3000 3000 3004 8036 8658 a33a
00c0 a889 1100 0000 a033 3200 c07b 601f
7e03 002b 211d d481 4734 a818
</span>

Since this is an UDP packet with port number 1719, this is a RAS message. First 20 octets are IP header (colored as light green) and next 8 octets UDP header (colored as purple). The rests (colored as red) are actual RAS/H.225.0 message and can be fed into h323decoder. The result of decoding is:

admissionRequest {
        requestSeqNum = 65507
        callType = pointToPoint <>
        callModel = gatekeeperRouted <>
        endpointIdentifier =  7 characters {
          006d 0061 0078 0032 0030 0030 0030        max2000
        }
        destinationInfo = 2 entries {
          [0]=e164 "117"
          [1]=e164 "117"
        }
        srcInfo = 2 entries {
          [0]=h323_ID  7 characters {
            006d 0061 0078 0032 0030 0030 0030        max2000
          }
          [1]=e164 "0353257007"
        }
        srcCallSignalAddress = ipAddress {
          ip =  4 octets {
            c0 a8 89 11
          }
          port = 0
        }
        bandWidth = 160
        callReferenceValue = 13106
        conferenceID =  16 octets {
          00 c0 7b 60 1f 7e 03 00 2b 21 1d d4 81 47 34 a8     {` ~  +!   G4
        }
        activeMC = FALSE
        answerCall = FALSE
        canMapAlias = FALSE
        callIdentifier = {
          guid =  16 octets {
            00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
          }
        }
        willSupplyUUIEs = FALSE
      }

Next example is as follows:

max2000.ascend.co.jp.15003 > max2000-2.ascend.co.jp.1720: P 1:5(4) ack 1 win 4380
<span style="color: #339966;">4500 002c 6c02 0000 4006 7b52 c0a8 8911
c0a8 8915</span> <span style="color: #800080;">3a9b 06b8 0289 4f8c c3b8 4ed0
5018 111c 61a5 0000</span> <span style="color: #ff6600;">0300 009e</span> 0000
max2000.ascend.co.jp.15003 > max2000-2.ascend.co.jp.1720: P 5:159(154) ack 1 win 4380
<span style="color: #339966;">4500 00c2 6d02 0000 4006 79bc c0a8 8911
c0a8 8915</span> <span style="color: #800080;">3a9b 06b8 0289 4f90 c3b8 4ed0
5018 111c 1690 0000</span> 0802 3332 0504 0388
90a5 7004 a131 3137 <span style="color: #0000ff;">7e00 8705</span> <span style="color: #ff0000;">40f8 0600
0891 4a00 0100 c0a8 8911 3a9c 0240 0600
6d00 6100 7800 3200 3000 3000 3004 8036
8658 a33a 28c0 b500 0014 224d 756c 7469
566f 6963 6520 4761 7465 7761 7920 666f
7220 7468 6520 4d41 5820 3630 3030 0437
2e30 2e30 0001 0100 44a0 c0a8 8915 06b8
0000 c07b 601f 7e03 002b 211d d481 4734
a800 0c07 00c0 a889 1100 0000 0503 4e55
4c4c
</span>

Since this is a TCP packet with a port number 1720, this is a Q.931/H.225.0 message. Note that these are two IP packets but these form a single Q.931/H.225.0 message. Similar to the first example, the first 20 octet is an IP header (colored as light green) and next 20 octets is a TCP header (colored as purple). Next 4 octets ‘0300 009e’ in the first IP packet is a TPKT header (colored as orange). Next ‘0000’ is a garbage (See the TCP sequence number of this packet) and you should ignore them. (These garbage octets are printed out because the minimum MTU size of Ethernet is 46 octets, and tcpdump prints out these octets anywa). Preceding octets up to ‘7e00 8705’ are Q.931 IEs. The rest part starting from ‘40f8 …’ (colored as red) are the Q.931/H.225.0 message. Only the part that is colored as red should be fed into the h323decoder. The result of decoding this hex dump is:

{
        h323_uu_pdu = {
          h323_message_body = setup {
            protocolIdentifier = 0.0.8.2250.0.1
            h245Address = ipAddress {
              ip =  4 octets {
                c0 a8 89 11
              }
              port = 15004
            }
            sourceAddress = 2 entries {
              [0]=h323_ID  7 characters {
                006d 0061 0078 0032 0030 0030 0030        max2000
              }
              [1]=e164 "0353257007"
            }
            sourceInfo = {
              vendor = {
                vendor = {
                  t35CountryCode = 181
                  t35Extension = 0
                  manufacturerCode = 20
                }
                productId =  35 octets {
                  4d 75 6c 74 69 56 6f 69 63 65 20 47 61 74 65 77   MultiVoice Gatew
                  61 79 20 66 6f 72 20 74 68 65 20 4d 41 58 20 36   ay for the MAX 6
                  30 30 30                                          000
                }
                versionId =  5 octets {
                  37 2e 30 2e 30                                    7.0.0
                }
              }
              gateway = {
              }
              mc = FALSE
              undefinedNode = FALSE
            }
              }
              [1]=e164 "0353257007"
            }
            sourceInfo = {
              vendor = {
                vendor = {
                  t35CountryCode = 181
                  t35Extension = 0
                  manufacturerCode = 20
                }
                productId =  35 octets {
                  4d 75 6c 74 69 56 6f 69 63 65 20 47 61 74 65 77   MultiVoice Gatew
                  61 79 20 66 6f 72 20 74 68 65 20 4d 41 58 20 36   ay for the MAX 6
                  30 30 30                                          000
                }
                versionId =  5 octets {
                  37 2e 30 2e 30                                    7.0.0
                }
              }
              gateway = {
              }
              mc = FALSE
              undefinedNode = FALSE
            }
            destinationAddress = 1 entries {
              [0]=e164 "117"
            }
            destCallSignalAddress = ipAddress {
              ip =  4 octets {
                c0 a8 89 15
              }
              port = 1720
            }
            activeMC = FALSE
            conferenceID =  16 octets {
              00 c0 7b 60 1f 7e 03 00 2b 21 1d d4 81 47 34 a8     {` ~  +!   G4
            }
            conferenceGoal = create <>
            callType = pointToPoint <>
            sourceCallSignalAddress = ipAddress {
              ip =  4 octets {
                c0 a8 89 11
              }
              port = 0
            }
          }
        }
        user_data = {
          protocol_discriminator = 5
          user_information =  4 octets {
            4e 55 4c 4c                                       NULL
          }
        }
      }

Final example is an H.245 message.

max2000-2.ascend.co.jp.15009 > max2000.ascend.co.jp.15004: P 1:90(89) ack 1 win 4380
<span style="color: #339966;">4500 0081 e14d 0000 4006 05b2 c0a8 8915
c0a8 8911</span> <span style="color: #993366;">3aa1 3a9c c3ba 2276 028b 29e9
5018 111c 20d7 0000</span> <span style="color: #ff6600;">0300 004e</span> <span style="color: #ff0000;">0270 0106
0008 8175 0002 800d 0000 3c00 0100 0001
0000 0100 0003 8000 0020 c03b 8000 0108
a817 6f40 0002 2200 0740 0003 09f8 0def
404a 3700 5040 0100 0080 0001 0100 0000
0201 0001 0003</span> <span style="color: #ff6600;">0300 000b</span> <span style="color: #ff0000;">0100 4680 824b
69
</span>

This is a TCP packet and uses ephemeral port number (1024 or greater) at both ends. H.245 messages usually look this way. IP header and TCP header are colored as light green and purple respectively. The next 4 octets are a TPKT header (colored as orange). But carefully examining the length field (‘004e’) in the TPKT header, this IP packets actually contains more than one H.245 messages (in fact, two)! This is yet another TCP difficulty as I described before. These two messages are a terminal capability set request and a master/slave determination request defined in H.245. Here’s the result of decoding of these messages:

request terminalCapabilitySet {
        sequenceNumber = 1
        protocolIdentifier = 0.0.8.245.0.2
        multiplexCapability = h2250Capability {
          maximumAudioDelayJitter = 60
          receiveMultipointCapability = {
            multicastCapability = FALSE
            multiUniCastConference = FALSE
            mediaDistributionCapability = 1 entries {
              [0]={
                centralizedControl = FALSE
                distributedControl = FALSE
                centralizedAudio = FALSE
                distributedAudio = FALSE
                centralizedVideo = FALSE
                distributedVideo = FALSE
              }
            }
          }
          transmitMultipointCapability = {
            multicastCapability = FALSE
            multiUniCastConference = FALSE
            mediaDistributionCapability = 1 entries {
              [0]={
                centralizedControl = FALSE
                distributedControl = FALSE
                centralizedAudio = FALSE
                distributedAudio = FALSE
                centralizedVideo = FALSE
                distributedVideo = FALSE
              }
            }
          }
          receiveAndTransmitMultipointCapability = {
            multicastCapability = FALSE
            multiUniCastConference = FALSE
            mediaDistributionCapability = 1 entries {
              [0]={
                centralizedControl = FALSE
                distributedControl = FALSE
                centralizedAudio = FALSE
                distributedAudio = FALSE
                centralizedVideo = FALSE
                distributedVideo = FALSE
              }
            }
          }
          mcCapability = {
            centralizedConferenceMC = FALSE
            decentralizedConferenceMC = FALSE
          }
          rtcpVideoControlCapability = FALSE
          mediaPacketizationCapability = {
            h261aVideoPacketization = FALSE
          }
        }
        capabilityTable = 4 entries {
          [0]={
            capabilityTableEntryNumber = 1
            capability = receiveAudioCapability g711Ulaw64k 60
          }
          [1]={
            capabilityTableEntryNumber = 2
            capability = receiveVideoCapability h261VideoCapability {
              qcifMPI = 3
              temporalSpatialTradeOffCapability = FALSE
              maxBitRate = 6000
              stillImageTransmission = FALSE
            }
          }
          [2]={
            capabilityTableEntryNumber = 3
            capability = receiveAudioCapability g7231 {
              maxAl_sduAudioFrames = 8
              silenceSuppression = FALSE
            }
          }
          [3]={
            capabilityTableEntryNumber = 4
            capability = receiveVideoCapability h263VideoCapability {
              sqcifMPI = 4
              qcifMPI = 16
              cifMPI = 16
              maxBitRate = 19000
              unrestrictedVector = FALSE
              arithmeticCoding = FALSE
              advancedPrediction = FALSE
              pbFrames = FALSE
              temporalSpatialTradeOffCapability = FALSE
              errorCompensation = FALSE
            }
          }
        }
        capabilityDescriptors = 1 entries {
          [0]={
            capabilityDescriptorNumber = 0
            simultaneousCapabilities = 2 entries {
              [0]=2 entries {
                [0]=1
                [1]=3
              }
              [1]=2 entries {
                [0]=2
                [1]=4
              }
            }
          }
        }
      }

request masterSlaveDetermination {
        terminalType = 70
        statusDeterminationNumber = 8538985
      }

6. What is ipdecode for?

ipdecode is a perl script that can be used with h323decoder. This script can intelligently examine the output of tcpdump, strip the unnecessary headers, and then pass it to h323decoder appropriately. Using this script, you don’t have to worry about the tedious procedure I described above. You can simply do as follows:

# tcpdump -s 1500 -x | ipdecode -Ph323

This will probably give you a desired result.

ipdecode is written and contributed by Yuzo Yamashita yyamashita@ascend.co.jp who is one of my coworkers.

Motonori Shindo mshindo@ascend.co.jp Tuesday, September 28, 1999 11:30:58 AM