The CoD4 Unleashed Server uses a sophisticated networking architecture based on the Quake 3 engine, with UDP for game traffic and TCP for administrative connections.
Network Transport
UDP Game Protocol
The primary game protocol uses UDP for low-latency communication:
typedef struct netchan_s {
netsrc_t sock;
netadr_t remoteAddress;
unsigned int qport;
int incomingSequence;
int outgoingSequence;
int dropped;
int fragmentSequence;
int fragmentLength;
byte * unsentBuffer;
int unsentBufferSize;
qboolean unsentFragments;
int unsentLength;
int unsentFragmentStart;
byte * fragmentBuffer;
int fragmentBufferSize;
} netchan_t ;
UDP is chosen for game traffic because low latency is more important than guaranteed delivery. Missing packets are handled through delta compression.
TCP Administrative Protocol
TCP connections are used for:
RCON (remote console) access
File downloads (optional)
Status queries
Statistics submission
#define MAX_TCPCONNECTIONS 120
#define MIN_TCPAUTHWAITTIME 320
#define MAX_TCPAUTHWAITTIME 3000
#define MAX_TCPCONNECTEDTIMEOUT 1800000 // 30 minutes
typedef struct {
netadr_t remote;
unsigned int lastMsgTime;
int connectionId;
int serviceId;
tcpclientstate_t state;
} tcpConnections_t ;
typedef struct {
fd_set fdr;
int highestfd;
int activeConnectionCount;
unsigned long long lastAttackWarnTime;
tcpConnections_t connections [MAX_TCPCONNECTIONS];
} tcpServer_t ;
Network Addresses
Address Types
typedef enum {
NA_BAD, // Invalid address
NA_LOOPBACK, // Loopback (127.0.0.1)
NA_BROADCAST, // Broadcast address
NA_IP, // IPv4 UDP
NA_IP6, // IPv6 UDP
NA_TCP, // IPv4 TCP
NA_TCP6, // IPv6 TCP
NA_MULTICAST6, // IPv6 multicast
NA_BOT // Bot (no network)
} netadrtype_t ;
Address Structure
typedef struct {
netadrtype_t type;
union {
byte ip [ 4 ]; // IPv4 address
byte ip6 [ 16 ]; // IPv6 address
};
unsigned short port;
unsigned long scope_id; // IPv6 scope
int sock; // Socket descriptor
} netadr_t ;
Socket Management
Opening Sockets
The server opens both IPv4 and IPv6 sockets:
void NET_OpenIP ( void ) {
int port = net_port -> integer ;
int port6 = net_port6 -> integer ;
if (port == 0 ) {
port = PORT_SERVER; // Default 28960
}
// Open IPv4 sockets
if ( net_enabled -> integer & NET_ENABLEV4) {
for (i = 0 ; i < numIP; i ++ ) {
if ( localIP [i]. type == NA_IP) {
ip_socket [i]. sock = NET_IPSocket (
net_ip -> string ,
port,
& err,
qfalse // UDP
);
}
}
}
// Open IPv6 sockets
if ( net_enabled -> integer & NET_ENABLEV6) {
for (i = 0 ; i < numIP; i ++ ) {
if ( localIP [i]. type == NA_IP6) {
ip_socket [i]. sock = NET_IP6Socket (
net_ip6 -> string ,
port6,
& boundto,
& err,
qfalse // UDP
);
}
}
}
}
Socket Options
// Make socket non-blocking
ioctlsocket (newsocket, FIONBIO, & _true );
// Enable broadcast (UDP only)
setsockopt (newsocket, SOL_SOCKET, SO_BROADCAST, & i , sizeof (i));
// Set IP Type of Service (QoS)
int tos = IPEFF_EF; // 0xB8 - Expedited Forwarding
setsockopt (newsocket, IPPROTO_IP, IP_TOS, & tos , sizeof (tos));
// Enable address reuse (TCP)
int reuse = 1 ;
setsockopt (newsocket, SOL_SOCKET, SO_REUSEADDR, & reuse , sizeof (reuse));
// IPv6 only (no IPv4-mapped)
setsockopt (newsocket, IPPROTO_IPV6, IPV6_V6ONLY, & i , sizeof (i));
The server sets TOS to Expedited Forwarding (0xB8) to request priority handling from routers.
Packet Structure
/*
Packet header:
-------------
4 bytes - outgoing sequence (high bit = fragmented)
[2 bytes - qport (client to server only)]
[4 bytes - fragment start byte]
[2 bytes - fragment length]
If sequence is -1, packet is out-of-band.
*/
#define PACKET_HEADER 10
#define FRAGMENT_BIT ( 1 << 31 )
#define FRAGMENT_SIZE 1248
Packet Flow
Sequence Number
4-byte sequence number for ordering
High bit indicates fragmentation
-1 (0xFFFFFFFF) for out-of-band packets
QPort (Client → Server)
2-byte client identifier
Helps identify client behind NAT
Allows port remapping detection
Fragment Info (if fragmented)
4 bytes: fragment start offset
2 bytes: fragment length
Last fragment has length < FRAGMENT_SIZE
Payload
Actual message data
Commands, entity states, player input
Huffman compressed (optional)
Netchan (Network Channel)
Initialization
void Netchan_Setup (
netsrc_t sock ,
netchan_t * chan ,
netadr_t adr ,
unsigned int qport ,
byte * unsentBuffer ,
int unsentBufferSize ,
byte * fragmentBuffer ,
int fragmentBufferSize
) {
memset (chan, 0 , sizeof ( netchan_t ));
chan -> sock = sock;
chan -> remoteAddress = adr;
chan -> qport = qport;
chan -> incomingSequence = 0 ;
chan -> outgoingSequence = 1 ;
chan -> unsentBuffer = unsentBuffer;
chan -> unsentBufferSize = unsentBufferSize;
chan -> fragmentBuffer = fragmentBuffer;
chan -> fragmentBufferSize = fragmentBufferSize;
}
Packet Processing
qboolean Netchan_Process ( netchan_t * chan , msg_t * msg ) {
int sequence;
qboolean fragmented;
// Read sequence number
MSG_BeginReading (msg);
sequence = MSG_ReadLong (msg);
// Check for fragmentation
if (sequence & FRAGMENT_BIT) {
sequence &= ~ FRAGMENT_BIT;
fragmented = qtrue;
} else {
fragmented = qfalse;
}
// Server reads qport
if ( chan -> sock == NS_SERVER) {
MSG_ReadShort (msg);
}
// Handle fragments
if (fragmented) {
int fragmentStart = MSG_ReadLong (msg);
int fragmentLength = MSG_ReadShort (msg);
// Validate fragment
if (fragmentStart != chan -> fragmentLength ) {
// Out of order, drop
return qfalse;
}
// Accumulate fragment
memcpy (
chan -> fragmentBuffer + chan -> fragmentLength ,
msg -> data + msg -> readcount ,
fragmentLength
);
chan -> fragmentLength += fragmentLength;
// If not last fragment, wait for more
if (fragmentLength == FRAGMENT_SIZE) {
return qfalse;
}
// Reassemble complete message
memcpy ( msg -> data + 4 , chan -> fragmentBuffer , chan -> fragmentLength );
msg -> cursize = chan -> fragmentLength + 4 ;
chan -> fragmentLength = 0 ;
}
// Discard out-of-order packets
if (sequence <= chan -> incomingSequence ) {
return qfalse;
}
// Track dropped packets
chan -> dropped = sequence - ( chan -> incomingSequence + 1 );
chan -> incomingSequence = sequence;
return qtrue;
}
Out-of-order packets are silently dropped. The game handles missing updates through delta compression and prediction.
Fragmentation
Transmitting Large Messages
qboolean Netchan_Transmit ( netchan_t * chan , int length , const byte * data ) {
// Check if fragmentation needed
if (length >= FRAGMENT_SIZE) {
chan -> unsentFragments = qtrue;
chan -> unsentLength = length;
memcpy ( chan -> unsentBuffer , data, length);
// Send first fragment
Netchan_TransmitNextFragment (chan);
return qtrue;
}
// Send as single packet
msg_t send;
byte send_buf [MAX_PACKETLEN];
MSG_Init ( & send, send_buf, sizeof (send_buf));
MSG_WriteLong ( & send, chan -> outgoingSequence );
chan -> outgoingSequence ++ ;
if ( chan -> sock == NS_CLIENT) {
MSG_WriteShort ( & send, qport -> integer );
}
MSG_WriteData ( & send, data, length);
return NET_SendPacket (
chan -> sock ,
send . cursize ,
send . data ,
& chan -> remoteAddress
);
}
Fragment Size
#define FRAGMENT_SIZE 1248
1248 bytes chosen to fit within typical MTU (1500 bytes) after headers:
Ethernet: 14 bytes
IP: 20 bytes
UDP: 8 bytes
Leaves ~1458 bytes usable, 1248 provides safety margin
Rate Limiting
Query Rate Limiting
Protects against DoS attacks on server queries:
typedef struct leakyBucket_s {
byte type;
union {
byte _4 [ 4 ]; // IPv4
byte _6 [ 16 ]; // IPv6
} ipv;
unsigned long long lastTime;
signed char burst;
long hash;
struct leakyBucket_s * prev, * next;
} leakyBucket_t ;
typedef struct {
int max_buckets;
int max_hashes;
leakyBucket_t * buckets;
leakyBucket_t ** bucketHashes;
int queryLimitsEnabled;
leakyBucket_t infoBucket;
leakyBucket_t statusBucket;
leakyBucket_t rconBucket;
} queryLimit_t ;
Leaky Bucket Algorithm
qboolean SVC_RateLimit ( leakyBucket_t * bucket , int burst , int period ) {
if (bucket != NULL ) {
unsigned long long now = com_uFrameTime;
int interval = now - bucket -> lastTime ;
int expired = interval / period;
int expiredRemainder = interval % period;
// Drain bucket over time
if (expired > bucket -> burst ) {
bucket -> burst = 0 ;
bucket -> lastTime = now;
} else {
bucket -> burst -= expired;
bucket -> lastTime = now - expiredRemainder;
}
// Check if under limit
if ( bucket -> burst < burst) {
bucket -> burst ++ ;
return qfalse; // Allow
}
}
return qtrue; // Deny
}
Cvars:
sv_queryIgnoreMegs - Memory for rate limiting (MB)
sv_queryIgnoreTime - Rate limit interval (ms)
Limits:
getinfo : 4 requests per sv_queryIgnoreTime
getstatus : 2 requests per sv_queryIgnoreTime
rcon : 1 request per 100ms (bad password)
Bucket pools:
Per-IP address buckets
Global buckets (info, status, rcon)
LAN addresses bypass rate limiting
Packet Reception
UDP Packet Handling
int NET_GetPacket ( netadr_t * net_from , void * net_message , int maxsize , int socket ) {
int ret;
struct sockaddr_storage from;
socklen_t fromlen;
if (socket != INVALID_SOCKET) {
fromlen = sizeof (from);
ret = recvfrom (
socket,
net_message,
maxsize,
0 ,
( struct sockaddr * ) & from,
& fromlen
);
if (ret == SOCKET_ERROR) {
int err = socketError;
if (err != EAGAIN && err != ECONNRESET) {
Com_PrintWarning ( "NET_GetPacket: %s \n " , NET_ErrorString ());
}
return - 1 ;
}
// Convert address
SockadrToNetadr (( struct sockaddr * ) & from, net_from, qfalse, socket);
// Check size
if (ret >= maxsize) {
Com_PrintWarning ( "Oversize packet from %s \n " , NET_AdrToString (net_from));
return - 1 ;
}
return ret;
}
return - 1 ;
}
Packet Routing
void SV_PacketEvent ( netadr_t * from , msg_t * msg ) {
client_t * cl;
int seq;
if ( msg -> cursize < 4 ) {
return ;
}
MSG_BeginReading (msg);
seq = MSG_ReadLong (msg);
// Check for connectionless packet
if (seq == - 1 ) {
SV_ConnectionlessPacket (from, msg);
return ;
}
// Read qport
unsigned short qport = MSG_ReadShort (msg);
// Find client
cl = SV_ReadPackets (from, qport);
if (cl == NULL ) {
NET_OutOfBandPrint (NS_SERVER, from, "disconnect" );
return ;
}
// Process netchan
if ( ! Netchan_Process ( & cl -> netchan , msg)) {
return ;
}
// Handle message
SV_ExecuteClientMessage (cl, msg);
}
Out-of-Band Messages
Out-of-band packets (sequence -1) are used for:
getchallenge - Request connection challenge
connect - Initiate connection
getstatus - Request server status
getinfo - Request server info
rcon <password> <command> - Remote console
challengeResponse - Send challenge token
connectResponse - Accept/reject connection
statusResponse - Server status data
infoResponse - Server info data
print - Console message
disconnect - Kick/disconnect
Challenge System
Prevents IP spoofing and connection flooding:
typedef struct {
netadr_t adr;
int challenge;
int clientChallenge;
int time;
int pingTime;
int firstTime;
char pbguid [ 33 ];
qboolean connected;
int ipAuthorize;
} challenge_t ;
Client requests challenge
Sends getchallenge packet
Server generates challenge
Creates random number, stores with client IP
Client connects with challenge
Includes challenge in connect packet
Server validates challenge
Matches challenge to IP, rejects if invalid
LAN Detection
The server identifies LAN clients for special handling:
qboolean Sys_IsLANAddress ( netadr_t * adr ) {
if ( adr -> type == NA_LOOPBACK) {
return qtrue;
}
if ( adr -> type == NA_IP || adr -> type == NA_TCP) {
// RFC1918 private ranges
if ( adr -> ip [ 0 ] == 10 ) // 10.0.0.0/8
return qtrue;
if ( adr -> ip [ 0 ] == 172 && ( adr -> ip [ 1 ] & 0x f0 ) == 16 ) // 172.16.0.0/12
return qtrue;
if ( adr -> ip [ 0 ] == 192 && adr -> ip [ 1 ] == 168 ) // 192.168.0.0/16
return qtrue;
if ( adr -> ip [ 0 ] == 127 ) // 127.0.0.0/8
return qtrue;
}
else if ( adr -> type == NA_IP6 || adr -> type == NA_TCP6) {
// Link-local
if ( adr -> ip6 [ 0 ] == 0x fe && ( adr -> ip6 [ 1 ] & 0x c0 ) == 0x 80 ) // fe80::/10
return qtrue;
// Unique local
if (( adr -> ip6 [ 0 ] & 0x fe ) == 0x fc ) // fc00::/7
return qtrue;
}
// Check against local network interfaces
for ( int i = 0 ; i < numIP; i ++ ) {
if ( localIP [i]. type == adr -> type ) {
// Compare with netmask
if ( SameNetwork (adr, & localIP [i])) {
return qtrue;
}
}
}
return qfalse;
}
LAN clients:
Bypass rate limiting
Can have higher maxRate
Shown in server browser as LAN
Network CVars
Key networking configuration variables:
net_enabled - Enable networking (bitmask)
1 = IPv4
2 = IPv6
4 = Prioritize IPv6
net_ip - Bind IPv4 address
net_ip6 - Bind IPv6 address
net_port - Server port (default 28960)
net_port6 - IPv6 port (default same as net_port)
sv_maxRate - Maximum client data rate
sv_queryIgnoreMegs - Rate limit memory
sv_queryIgnoreTime - Rate limit interval
sv_floodProtect - Enable flood protection
showpackets - Show sent/received packets
showdrop - Show dropped packets
sv_shownet - Show network activity
sv_debugRate - Debug rate throttling
Network Security
Cookie Challenge
Prevents connection flooding:
int NET_CookieHash ( netadr_t * from ) {
uint32_t digest [ 5 ];
uint32_t workspace [ 80 ];
char data [ 64 ];
if ( from -> type == NA_IP) {
// Hash IP + port + secret
memcpy (data, from -> ip , 4 );
memcpy (data + 4 , & from -> port , 2 );
memcpy (data + 6 , net_cookieSecret, 58 );
} else if ( from -> type == NA_IP6) {
memcpy (data, from -> ip6 , 16 );
memcpy (data + 16 , & from -> port , 2 );
memcpy (data + 18 , net_cookieSecret, 46 );
}
sha_init (digest);
sha_transform (digest, data, workspace);
return digest [ 0 ];
}
The cookie secret is randomly generated on server start and changes on restart. Cookies from previous sessions are invalid.
Server Architecture Overall server structure
Configuration Network-related cvars