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)
- 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
lengthfield. Thecrcfield is computed the same, but spread across4bytes.
| 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)
- 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)
- 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).