Skip to content

P3 Packets

Initially used over dialup modems, P3 was designed to be a link layer protocol. However some client software supports TCP/IP in addition to dialup. In these cases P3 is still used, however it is encapsulated in the payload portion of TCP.

                             │◀──── P3 ───▶│
┌─────────────┬──────────────┬─────────────┐
│  IP Header  │  TCP Header  │ TCP Payload │
└─────────────┴──────────────┴─────────────┘

When encapsulated inside TCP, some features of P3 are redundant (e.g. CRC computation). Rather than create a new protocol, the existing P3 code was largely reused. Thus decoding P3 traffic sent over modem or over TCP/IP is the essentially the same.

Packet Fields

A P3 packet consists of the following fields in big endian (network) byte order: (1)

  1. This is the format of a P3 packet for the America Online client. P3 with the Quantum Link client is similar, but does not include a length field. The crc field is computed the same, but spread across 4 bytes.
Byte Offset Length Type Field
0x00 0x01 uint8 sync
0x01 0x02 uint16 crc
0x03 0x02 uint16 length
0x05 0x01 uint8 tx_seq
0x06 0x01 uint8 rx_seq
0x07 0x01 uint8 type
0x08 varies raw bytes data
last byte 0x01 uint8 msg_end

Here is an example P3 packet with the fields labeled:

      crc      tx_seq type             msg_end
       │          │     │                 │   
       ▼          ▼     ▼                 ▼   
    ┌─────┐     ┌──┐  ┌──┐              ┌──┐  
  5A F1 02 00 3C 10 7F A0 44 64 .. .. .. 0D   
 └──┘     └─────┘  └──┘  └──────────────┘     
   ▲         ▲       ▲           ▲            
   │         │       │           │            
 sync     length  rx_seq       data           

sync

The sync field is always the value 0x5A

crc

The crc field is a 16-bit CRC computed over the packet, starting from the first byte of the length field, going up to (but not including) the msg_end field.

Graphically:

       ┌──computed over──┐                   
       │                 ▼                   
       │  ┌─────────────────────────────┐    
 ┌──┬─────┬─────┬──┬──┬──┬──────────────┬──┐ 
 │5A│F1 02│00 3C│10│7F│A0│44 64 .. .. ..│0D│ 
 └──┴─────┴─────┴──┴──┴──┴──────────────┴──┘ 
  ▲    ▲     ▲   ▲   ▲  ▲        ▲       ▲   
  │    │     │   │   │  │        │       │   
sync  crc length │rx_seq│       data  msg_end
                 │      │                    
              tx_seq  type                   

The specific CRC algorithm used is CRC16-ARC. If developing your own tools, you can verify the results using this online CRC calculator. (1)

  1. Make sure to click the CRC-16 button, and look at the result for CRC-16/ARC.

An example implementation in Python, using a lookup table can be found in the PyOL project.

length

This is the length of the tx_seq, rx_seq, type, and data fields.

Graphically:

             ┌────covers────┐                
             │              ▼                
             │  ┌───────────────────────┐    
 ┌──┬─────┬─────┬──┬──┬──┬──────────────┬──┐ 
 │5A│F1 02│00 3C│10│7F│A0│44 64 .. .. ..│0D│ 
 └──┴─────┴─────┴──┴──┴──┴──────────────┴──┘ 
  ▲    ▲     ▲   ▲   ▲  ▲        ▲       ▲   
  │    │     │   │   │  │        │       │   
sync  crc length │rx_seq│       data  msg_end
                 │      │                    
              tx_seq  type                   

The minimal value for this field is 3, since a packet with no data will still have the tx_seq, rx_seq, and type fields. Therefore, to compute the length of just the data field, take the value of the length field and subtract three.

tx_seq and rx_seq

These fields are the transmission (tx_seq) and receive (rx_seq) sequence numbers. They are used to determine the proper ordering of messages, acknowledge which messages have been received by each side, and for flow control.

The tx_seq field is incremented by one for each DATA packet that is transmitted. (1)

  1. This is unlike TCP sequence numbers, which are incremented based on the size of data transmitted.

The rx_seq field is an acknowledgement of all packets up to and including the value in the rx_seq field. More information and examples can be found on the sliding window page.

type

The type field indicates the type of P3 packet, and how to interpret the DATA field (if present). The following are types are currently defined:

Value Type of Packet
0x20 DATA
0x21 SS
0x22 SSR
0x23 INIT
0x24 ACK
0x25 NAK
0x26 HEARTBEAT

To detect packets that have been reflected back (e.g. due to a poor quality transmission medium) the type field for all packets sent from the client have the high bit set. That is, the field is bitwise-OR'd with the value 0x80.

To interpret the packet type field transmitted across the wire, ignore the high bit by doing a bitwise-AND with the value 0x7F. For example if the value of the type field is 0xA3, this is an INIT packet since 0x23 == 0xA3 & 0x7F

data

The data field is used to transmit both information from higher layers (e.g tokens) and some P3 control information (e.g a NAK reason code).

msg_end

This is the last byte of the packet. It is always the value 0x0D.

Packet Types

This section covers the different types of packets in more depth.

INIT Packets

These packets are used to "initialize" (start) communication between the client and server. They are the first packet the client sends after a connection is established.

The data in an INIT packet is laid out as follows:

Note

This is the layout of an INIT packet for the 32-bit version of the 3.0 client. Other versions have a different layout.

Offset Length Type Field
0x00 0x01 uint8 platform
0x01 0x01 uint8 version_num
0x02 0x01 uint8 sub_version_num
0x03 0x01 uint8 unused
0x04 0x01 uint8 machine_memory
0x05 0x01 uint8 app_memory
0x06 0x02 uint16 pc_type
0x08 0x01 uint8 release_month
0x09 0x01 uint8 release_day
0x0A 0x02 uint16 customer_class
0x0C 0x04 uint32 udo_timestamp
0x10 0x02 uint16 dos_version
0x12 0x02 uint16 session_flags
0x14 0x01 uint8 video_type
0x15 0x01 uint8 processor_type
0x16 0x04 uint32 media_type
0x1A 0x04 uint32 windows_version
0x1E 0x01 uint8 memory_mode
0x1F 0x02 uint16 horizontal_res
0x21 0x02 uint16 vertical_res
0x23 0x02 uint16 num_colors
0x25 0x01 uint8 filler
0x26 0x02 uint16 region
0x28 0x08 uint16[4] language
0x30 0x01 uint8 connect_speed

ACK Packets

ACK packets are used to acknowledge the tx_seq of the most recent, correctly received packet. Unlike TCP, P3 ACK packets are not sent for every tx_seq change. Instead, a P3 ACK is sent if a receiver buffer passes a certain threshold, and in response to INIT and HEARTBEAT packets.

NAK Packets

NAK packets are sent in response to invalid packets. This can be if the packet has an invalid crc field, if the packet is received out of sequence (as determined by tx_seq) or if the packet is too large.

The payload of a NAK packet decribes the error condition. The values are:

Value Meaning
0x01 Invalid crc
0x02 Incorrect sequence
0x03 Incorrect length

DATA Packets

DATA Packets usually form the bulk of P3 traffic. The first two bytes of a DATA packet contain a token, which determines where and how the rest of the payload is processed.

HEARTBEAT Packets

HEARTBEAT packets are sent when a previously sent DATA packet is unacknowledged by the receiver after a certain time. An ACK packet is sent in response. See the sliding window page for more details.

SS Packets

An SS packet ("synchronize sequence"?) is used to elicit an SSR packet from the other side.

SSR Packets

An SSR packet ("synchronize sequence response"?) is the response to an SS packet. It instructs the receiver to resend messages not received by the sender (of the SSR).