What happened:
The Unreal Engine plugin's QuilkinPacketHandler appears to be incorrectly serializing the TArray<uint8> routing token. When appending the token as a suffix to outgoing packets, it consistently skips the first byte of the token.
This causes the Quilkin proxy, when configured with a suffix capture filter, to read a misaligned token. This misaligned token then fails to match the correct token in the proxy's routing table (which was set by our matchmaker), causing all client connection attempts to fail with a "no endpoint matched token" error.
What you expected to happen:
We expect the QuilkinPacketHandler to append the full 16-byte token to the outgoing packet. The proxy should then read this exact 16-byte token from the suffix, find a match in its routing table, and successfully forward the packet to the dedicated game server.
How to reproduce it (as minimally and precisely as possible):
- Configure a Matchmaker (like agones-pubsub-allocator) to generate a 16-byte token and register it with the Quilkin proxy's routing table.
- Configure the Quilkin proxy
ConfigMap to capture a 16-byte token from the suffix of the packet. (See Custom filters section below for YAML).
- Configure an Unreal Engine 5.5 client to use the
QuilkinConfigSubsystem and QuilkinPacketHandler.
- Have the client request a token from the Matchmaker.
- On the client, receive the 16-byte token, decode it from Base64 into a
TArray<uint8>, and set it using UQuilkinConfigSubsystem::SetRoutingToken().
- Attempt to
ClientTravel to the Quilkin proxy address.
- The connection will fail. The client will log a timeout, and the proxy will log a "no endpoint matched token" error.
Anything else we need to know?:
This is a byte-level serialization bug. The system is fully synchronized on a 16-byte token, but the proxy log proves it is reading a token that is offset by one byte.
Byte-Level Proof
-
Token from Matchmaker (The "Truth"):
- Base64:
bFJUU0tMZTRzS1FZYnFvMA==
- Decoded:
lRTSKLe4sKQYbqo0
-
Token the UE Client Initializes With (Correct):
- Log:
LogQuilkin: Display: Initialising PacketHandler: ... Routing Token: bFJUU0tMZTRzS1FZYnFvMA==
- Hex:
[0x6C, 0x52, 0x54, 0x53, ... , 0x6F, 0x30]
-
Token the Proxy Receives (Incorrect - 1 Byte Offset):
- Log:
...error":"filter no endpoint matched token \UlRTS0xlNHNLUVlicW8wAA==`"`
- Decoded:
RTSKLe4sKQYbqo0\x00
- Hex:
[0x52, 0x54, 0x53, 0x4B, ... , 0x30, 0x00]
Comparison:
| Array |
Byte 1 |
Byte 2 |
Byte 3 |
... |
Byte 15 |
Byte 16 |
| Sent |
0x6C (l) |
0x52 (R) |
0x54 (T) |
... |
0x6F (o) |
0x30 (0) |
| Read |
0x52 (R) |
0x54 (T) |
0x53 (S) |
... |
0x30 (0) |
0x00 (Junk) |
This shows the plugin is failing to write the first byte (0x6C) of the token to the packet. The investigation should focus on the QuilkinPacketHandler's PreSend or LowLevelSend implementation, specifically where the RoutingToken TArray is being copied into the packet buffer.
Environment:
- Quilkin version:
0.9.0 (from proxy log {"message":"Starting Quilkin","version":"0.9.0",...)
- Execution environment (binary, container, etc): Container (running in Kubernetes)
- Operating system: Linux (container base)
- Custom filters? (Yes/No - if so, what do they do?):
Yes, the standard token capture/router configuration.
apiVersion: v1
kind: ConfigMap
metadata:
name: quilkin-xds-filter-config
data:
quilkin.yaml: |
version: v1alpha1
filters:
- name: quilkin.filters.capture.v1alpha1.Capture
config:
metadataKey: "quilkin.dev/token"
suffix:
size: 16
remove: true
- name: quilkin.filters.token_router.v1alpha1.TokenRouter
config:
metadataKey: "quilkin.dev/token"
Log(s):
Matchmaker (Allocator) Log:
JSON
{"level":"info", ... "token":"bFJUU0tMZTRzS1FZYnFvMA==", ... "message":"controller: updating GameServer with routing token"}
Unreal Client Log:
LogServer: Display: Starting with token: bFJUU0tMZTRzS1FZYnFvMA==
LogServer: Display: Decoded token to 16 bytes, Token: lRTSKLe4sKQYbqo0
LogQuilkin: Display: Initialising PacketHandler: Packet Handling: Enabled, Routing Token: bFJUU0tMZTRzS1FZYnFvMA==
LogQuilkin: Display: 10.0.10.14:7600 exceeded WaitForRead timeout 6 times
Quilkin Proxy Log:
JSON
{"timestamp":"2025-10-25T18:08:49.789103Z","level":"WARN","fields":{"message":"pipeline report","error":"filter no endpoint matched token UlRTS0xlNHNLUVlicW8wAA==","instances":"6"},"target":"quilkin::components::proxy::packet_router","filename":"src/components/proxy/packet_router.rs","threadId":"ThreadId(2)"}
Others: N/A
What happened:
The Unreal Engine plugin's
QuilkinPacketHandlerappears to be incorrectly serializing theTArray<uint8>routing token. When appending the token as asuffixto outgoing packets, it consistently skips the first byte of the token.This causes the Quilkin proxy, when configured with a
suffixcapture filter, to read a misaligned token. This misaligned token then fails to match the correct token in the proxy's routing table (which was set by our matchmaker), causing all client connection attempts to fail with a "no endpoint matched token" error.What you expected to happen:
We expect the
QuilkinPacketHandlerto append the full 16-byte token to the outgoing packet. The proxy should then read this exact 16-byte token from the suffix, find a match in its routing table, and successfully forward the packet to the dedicated game server.How to reproduce it (as minimally and precisely as possible):
ConfigMapto capture a 16-byte token from thesuffixof the packet. (SeeCustom filterssection below for YAML).QuilkinConfigSubsystemandQuilkinPacketHandler.TArray<uint8>, and set it usingUQuilkinConfigSubsystem::SetRoutingToken().ClientTravelto the Quilkin proxy address.Anything else we need to know?:
This is a byte-level serialization bug. The system is fully synchronized on a 16-byte token, but the proxy log proves it is reading a token that is offset by one byte.
Byte-Level Proof
Token from Matchmaker (The "Truth"):
bFJUU0tMZTRzS1FZYnFvMA==lRTSKLe4sKQYbqo0Token the UE Client Initializes With (Correct):
LogQuilkin: Display: Initialising PacketHandler: ... Routing Token: bFJUU0tMZTRzS1FZYnFvMA==[0x6C, 0x52, 0x54, 0x53, ... , 0x6F, 0x30]Token the Proxy Receives (Incorrect - 1 Byte Offset):
...error":"filter no endpoint matched token \UlRTS0xlNHNLUVlicW8wAA==`"`RTSKLe4sKQYbqo0\x00[0x52, 0x54, 0x53, 0x4B, ... , 0x30, 0x00]Comparison:
0x6C(l)0x52(R)0x54(T)0x6F(o)0x30(0)0x52(R)0x54(T)0x53(S)0x30(0)0x00(Junk)This shows the plugin is failing to write the first byte (
0x6C) of the token to the packet. The investigation should focus on theQuilkinPacketHandler'sPreSendorLowLevelSendimplementation, specifically where theRoutingTokenTArrayis being copied into the packet buffer.Environment:
0.9.0(from proxy log{"message":"Starting Quilkin","version":"0.9.0",...)Yes, the standard token capture/router configuration.
{"level":"info", ... "token":"bFJUU0tMZTRzS1FZYnFvMA==", ... "message":"controller: updating GameServer with routing token"}
Unreal Client Log:
LogServer: Display: Starting with token: bFJUU0tMZTRzS1FZYnFvMA==
LogServer: Display: Decoded token to 16 bytes, Token: lRTSKLe4sKQYbqo0
LogQuilkin: Display: Initialising PacketHandler: Packet Handling: Enabled, Routing Token: bFJUU0tMZTRzS1FZYnFvMA==
LogQuilkin: Display: 10.0.10.14:7600 exceeded WaitForRead timeout 6 times
Quilkin Proxy Log:
JSON
{"timestamp":"2025-10-25T18:08:49.789103Z","level":"WARN","fields":{"message":"pipeline report","error":"filter no endpoint matched token
UlRTS0xlNHNLUVlicW8wAA==","instances":"6"},"target":"quilkin::components::proxy::packet_router","filename":"src/components/proxy/packet_router.rs","threadId":"ThreadId(2)"}Others: N/A