Link32 is a tactical communication protocol inspired by VMF, LINK16, TSM, SRW, and MQTT, designed for military applications requiring low-latency, secure, and scalable data exchange in contested environments. It supports swarm coordination, real-time position location information (PLI), command and control (C2), and tactical chat over UDP-based multicast networks.
Skynet is the reference server implementing Link32 tactical battlefield protocol implemented in pure C99 with a single OpenSSL dependency (to be replaced in real applcations).
The Skynet Message structure is compact, designed for swarms of thousands of nodes:
typedef struct {
uint8_t version : 4; // Protocol version (current: 1)
uint8_t type : 4; // Message type (0-6)
uint8_t qos : 4; // Quality of Service (0-3)
uint8_t hop_count : 4; // Hop count for routing (0-15)
uint32_t npg_id; // Topic identifier (1-103)
uint32_t node_id; // Sender node ID (FNV-1a hash)
uint32_t seq_no; // Sequence number for deduplication
uint8_t iv[16]; // AES-256-GCM initialization vector
uint16_t payload_len; // Payload length (0-32767)
uint8_t payload[MAX_BUFFER]; // Encrypted payload + 16-byte GCM tag
} SkyNetMessage;
Id | Type | Description |
---|---|---|
0 | Key Exchange | Exchanges ECC public keys for ECDH session setup. |
1 | Slot Request | Requests a TDMA slot from the server. |
2 | Chat | Sends tactical chat messages. |
3 | Ack | Acknowledges slot assignments or other control messages. |
4 | Waypoint | Specifies navigation waypoints for C2. |
5 | Status | Reports position, velocity, or sensor data (e.g., PLI). |
6 | Formation | Coordinates swarm formations. |
NPG | Name | Multicast | Purpose |
---|---|---|---|
1 | npg_control | 239.255.0.1 | Handles key exchange and slot requests for network control. |
6 | npg_pli | 239.255.0.6 | Processes status messages for position information . |
7 | npg_surveillance | 239.255.0.7 | Forwards status messages with sensor data to subscribers. |
29 | npg_chat | 239.255.0.29 | Relays chat and ack messages for tactical communication. |
100 | npg_c2 | 239.255.0.100 | Processes waypoint and formation messages for C2. |
101 | npg_alerts | 239.255.0.101 | Status messages for network alerts and self-healing. |
102 | npg_logistics | 239.255.0.102 | Handles status and chat for logistical coordination. |
103 | npg_coord | 239.255.0.103 | Relays chat, waypoint, and swarm coordination. |
Link32 uses a minimalistic Time Division Multiple Access (TDMA)-like slot manager to reduce message collisions and emulate dynamic topics:
slots[SLOT_COUNT=256]
) in ServerState
, where each slot is either free (0) or assigned to a node_id
.MAX_TOPICS=8
).TIME_SLOT_INTERVAL_US=1000µs
, synchronized via a timerfd in the server.Implementation details:
SKYNET_MSG_SLOT_REQUEST
(type 1) with their node_id
to 239.255.0.1
(NPG 1).assign_slot
and responds with a SKYNET_MSG_ACK
(type 3) containing the slot ID (4-byte payload).239.255.1.<slot_id % 256>
) and sends messages to it.To prevent message loops and duplicates, the server uses a fixed-size circular buffer seq_cache
for deduplication:
{node_id, seq_no, timestamp}
.Implementation details:
node_id ^ seq_no
via FNV-1a) to an index in seq_cache
.~/.skynet/ecc/secp384r1/<node_hash>.{ec_priv,ec_pub}
.~/.skynet_client/ecc/secp384r1/<node_hash>.{ec_priv,ec_pub}
msg->node_id == state->node_id
to prevent decryption errors and loops.Nodes subscribe to topics based on their role, joining the corresponding multicast groups:
Role | NPGs Subscribed | Purpose |
---|---|---|
Infantry | 1, 29 | Network control and tactical chat. |
Drone | 1, 6, 7, 100, 101 | Control, PLI, surveillance, C2, and alerts. |
Air | 1, 6, 7, 100, 101, 103 | Control, PLI, surveillance, C2, alerts, and coordination. |
Sea | 1, 7, 29, 102, 103 | Control, surveillance, chat, logistics, and coordination. |
Ground | 1, 7, 29, 102 | Control, surveillance, chat, and logistics. |
Relay | 1, 6, 101 | Control, PLI, and alerts for message relaying. |
Controller | 1, 6, 100, 101 | Control, PLI, C2, and alerts for command posts. |
$ git clone git@github.com:BitEdits/skynet
$ cd skynet
$ gcc -o skynet_client skynet_client.c skynet_proto.c -lcrypto
$ gcc -o skynet skynet.c skynet_proto.c -pthread -lcrypto
Link32 deploys via a provisioning script (skynet.sh), which generates ECC key pairs for all network nodes and topics. Public keys must be manually copied to client key stores for mutual authentication.
$ ./skynet.sh
Generated keys for node npg_control (hash: 06c5bc52) in /home/user/.skynet/ecc/secp384r1/
Generated keys for node npg_pli (hash: c9aef284) in /home/user/.skynet/ecc/secp384r1/
Generated keys for node npg_surveillance (hash: 4d128cdc) in /home/user/.skynet/ecc/secp384r1/
Generated keys for node npg_chat (hash: 9c69a767) in /home/user/.skynet/ecc/secp384r1/
Generated keys for node npg_c2 (hash: 89f28794) in /home/user/.skynet/ecc/secp384r1/
Generated keys for node npg_alerts (hash: 9f456bca) in /home/user/.skynet/ecc/secp384r1/
Generated keys for node npg_logistics (hash: 542105cc) in /home/user/.skynet/ecc/secp384r1/
Generated keys for node npg_coord (hash: e46c0c22) in /home/user/.skynet/ecc/secp384r1/
Generated keys for node server (hash: 40ac3dd2) in /home/user/.skynet/ecc/secp384r1/
Generated keys for node client (hash: 8f929c1e) in /home/user/.skynet_client/ecc/secp384r1/
$ cp ~/.skynet/ecc/secp384r1/*.ec_pub ~/.skynet_client/ecc/secp384r1/
The server skynet
binds to UDP port 6566, joins multicast groups for all topics 239.255.0.<npg_id>
,
and processes incoming messages using a global network queue (MessageQueue mq). It spawns worker
threads pinned to CPU cores for concurrent message handling.
Each topic has a dedicated subscriber queue topic_queues
,
and messages are forwarded to slot-specific multicast groups 239.255.1.<slot_id>
for dynamic topics.
Example server output:
$ ./skynet server
Node 40ac3dd2 bound to 0.0.0.0:6566.
Joined multicast group 239.255.0.1 (NPG 1: control).
Joined multicast group 239.255.0.6 (NPG 6: PLI).
...
Message received, from=8f929c1e, to=1, size=231.
Decryption successful, from=8f929c1e, to=1, size=215.
Saved public key for client 8f929c1e.
Assigned slot 0 to node 8f929c1e.
Message received, from=8f929c1e, to=6, size=40.
Decryption successful, from=8f929c1e, to=6, size=24.
Message sent from=8f929c1e, to=6, seq=3, multicast=239.255.1.0, latency=36643.
The client skynet_client
connects to port 6566, joins topic-specific multicast groups, and sends:
Example client output:
$ ./skynet_client client
Node 8f929c1e connecting to port 6566.
Joined multicast group 239.255.0.1 (NPG 1).
Joined multicast group 239.255.0.6 (NPG 6).
...
Sent key exchange message to server.
Sent slot request message to server.
Received slot assignment: slot=0.
Joined slot multicast group 239.255.1.0.
Sent status message: pos=[0.1, 0.1, 0.1], vel=[0.0, 0.0, 0.0], seq=2, multicast=239.255.1.0.
Skynet distribution contains 5 binary files.
skynet_keygen <node> [--server|--client]
Generates ECC secp384r1 key pairs for the specified node,
storing them in ~/.skynet/ecc/secp384r1/
(server) or ~/.skynet_client/ecc/secp384r1/
(client).
skynet_encrypt <sender> <recipient> <file>
Encrypts a test message from <sender>
to <recipient>
for the specified NPG, producing <npg_id>.sky
.
skynet_decrypt <sender> <recipient> <file.sky>
Decrypts <file>
(e.g., <npg_id>.sky
) using keys for <sender>
and <recipient>
.
skynet <node>
Runs the server named as <node>
, FNV1A 32-bit hash is used.
The server assigns a slot, forwards status messages to 239.255.1.<slot_id>
, and logs all activity.
skynet_client <node>
Runs the client named as <node>
, FNV1A 32-bit hash is used.
The client sends key exchange, slot request, and status messages.
SLOT_COUNT=256
limits dynamic topics to 256 nodes.SEQ_CACHE_SIZE=1024
may lead to cache collisions in high-traffic scenarios.