Networking system

From ioquake3 wiki
Jump to: navigation, search

The networking system consists of clients sending a sequence of UDP packets to the server, and vice versa. Network packets come in one of two general classes: netchan messages and out-of-band messages. Netchan messages are messages that are sent between a server and a client that have negotiated a connection. Out-of-band (OOB) messages are sent between a server and a client who have not yet established a netchan connection. For instance, OOB messages are sent by a client when it first starts up to authenticate itself with a server.

Out-of-band messages

Out-of-band messages are created either with NET_OutOfBandPrint() or NET_OutOfBandData(). OOB messages are identified as starting with four 0xFF bytes. OOB messages have a fairly simple format. An OOB message is contained in a single UDP packet, where everything after the four byte header is a single command, formatted as a NUL-terminated string. OOB commands lists the commands used in OOB messages. One twist is that NET_OutOfBandData() compresses the command using Huffman coding.

out-of-band format

32 bits 0xFFFFFFFF out-of-band indicator
string command


Netchan messages

Any packet that is not identified with the 0xFFFFFFFF out-of-band header is assumed to be part of a netchan message. A single netchan message may be fragmented into several UDP packets. The netchan headers in the UDP packets are generated by Netchan_Transmit(), or Netchan_TransmitNextFragment() for fragmented messages. All netchan UDP packets have the following format:

netchan UDP packet format

1 bit fragment indicator
31 bits message sequence number
if packet is from client to server:
16 bits client's port number
if {fragment indicator} == 0x1:
16 bits fragment offset
16 bits fragment length
data message content

Each netchan message has a unique sequential number. This sequence number is used to detect lost messages. If a message is fragmented into several UDP packets, each packet will have the same sequence number. All of the data that follows the header in the packet is the actual message content, which is described below.

fragments

If the length of a message exceeds FRAGMENT_SIZE (1300 bytes), then the message content is fragmented into several UDP packets.

In the packet header for a fragment, the fragment offset specifies the fragment's offset within the entire message content. For example, if a 90 byte message is divided into three 30 byte fragments, the packet header for the first fragment would have fragment offset=0, the header for the second fragment would have fragment offset=30, and the header for the last fragment would have fragment offset=60.

The fragment length specifies how much of the message content is included in the fragment. In the example given above, each fragment's fragment length would be 30.

The receiver knows that it has received the last fragment of a message when fragment length < FRAGMENT_SIZE. To reconstruct the entire message content, the receiver appends the message content of each fragment in the order specified by fragment offset.

message content

The content of netchan messages is much more complex than that of OOB messages. The structure of a netchan message differs depending on whether it's a server message or a client message. The server message format page documents the data structure of messages sent from a server to its clients, whereas the client message format page documents the data structure of messages sent from a client to its server.

Keep in mind that the server message format and client message format pages document the data structure of the message before it is compressed and encoded. The message content is compressed using Huffman coding and is then encoded to prevent tampering.

message encoding

Since UDP packets are connectionless, servers and clients encode each outgoing netchan message in order to prevent third parties from sending spoofed UDP packets, pretending to be the server or a client.

Servers use SV_Netchan_Encode() to encode outgoing messages to clients. SV_Netchan_Encode() will encode all but the first 4 bytes of the message (the ID of last client command received) by xor'ing it with a key that is unique for each message. The key is generated by xor'ing a combination of the client's connection challenge key, the sequence number of the netchan message, and the contents of the last command received from the client, which corresponds to the ID specified in the first 4 unencoded bytes of the message. See SV_Netchan_Encode() for more details.

Clients similarly use CL_Netchan_Encode() to encode outgoing messages to the server. CL_Netchan_Encode() will xor all but the first 12 bytes of the message (the server's ID, the ID of the last server message received, and the ID of last server command received). The key is generated by xor'ing a combination of the client's connection challenge key, the server's ID, the ID of the last server message received, and the contents of the last command received from the server, which corresponds to the ID specified in the last 4 bytes of the 12 unencoded bytes of the message. See CL_Netchan_Encode() for more details.

message pipeline

To clarify in what order a message compressed, encoded, and fragmented, the following pipeline illustrates the steps in order. It starts with messages in the format documented by the server message format/client message format pages and ends with the actual bytes sent in a UDP packet.

raw message → Huffman compression → encoding → [optional fragmentation] → netchan packet headers added → UDP send

See also