Jump Into CRDP aka CloudRouter Discovery Protocol
Every router needs to answer a simple operator question: “what else is on this link?” LLDP already solves neighbour discovery on wired ethernet, but it rides a reserved layer-2 multicast destination that many bridges and virtual switches filter or consume, so the frames never reach the peer. CRDP (CloudRouter Discovery Protocol) keeps LLDP’s well-understood TLV payload and simply carries it inside ordinary IPv6 link-local multicast UDP, so the beacon traverses bridges like any other multicast packet while staying decodable by any LLDP-aware tool.
This post is written as a short RFC-style specification. It describes CRDP version 1: the on-wire contract a node emits and accepts.
1. Terminology
The key words MUST, MUST NOT, SHOULD, and MAY are used as defined in RFC 2119.
- Beacon: one CRDP datagram advertising a node on one interface.
- Node: a CloudRouter instance participating in CRDP on a link.
- Chassis ID: a stable per-node identifier carried in the Chassis ID TLV.
- Neighbour: a remote node observed via a received beacon.
- LLDPDU: the LLDP Data Unit, i.e. the TLV chain that forms the beacon body.
2. Transport
CRDP runs over UDP on IPv6 only.
| Parameter | Value |
|---|---|
| Address family | IPv6 |
| Multicast group | ff02::ffe:1 (link-local scope) |
| UDP port | 4224 (source and destination) |
| Hop limit | 1 |
| Scope | Single link; frames MUST NOT be forwarded |
A node binds one socket to [::]:4224 and joins ff02::ffe:1 on every
interface it beacons on. Sends set IPV6_MULTICAST_IF to pin the egress
interface, IPV6_MULTICAST_HOPS=1 so the frame cannot leave the link, and
disable IPV6_MULTICAST_LOOP so a node does not receive its own beacons.
Because the group is link-local (ff02::) and the hop limit is 1, CRDP is
strictly a single-link protocol. There is no relaying, no proxying, and no
routing of beacons.
3. Beacon Body: LLDP TLV Chain
The UDP payload is a standard IEEE 802.1AB LLDPDU carried verbatim. No additional framing is added around the TLV chain (obfuscation, described in Section 7, is a separate optional wrapper).
Each TLV has a 16-bit big-endian header: a 7-bit type in the high bits and a
9-bit length in the low bits, followed by length payload bytes.
0 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| type (7) | length (9) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| value (length bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
The 9-bit length field caps any single TLV value at 511 bytes.
3.1 TLV Types
CRDP uses the standard LLDP TLV type numbers:
| Type | Name | Presence |
|---|---|---|
| 0 | End of LLDPDU | Mandatory (terminator) |
| 1 | Chassis ID | Mandatory |
| 2 | Port ID | Mandatory |
| 3 | Time To Live | Mandatory |
| 5 | System Name | Optional |
| 6 | System Description | Optional |
| 7 | System Capabilities | Optional |
| 8 | Management Address | Optional, repeatable |
| 127 | Organizationally Specific | Ignored on receive |
A beacon MUST contain the three mandatory TLVs (Chassis ID, Port ID, TTL) and MUST end with the End-of-LLDPDU TLV (type 0, length 0). A receiver MUST ignore any TLV type it does not understand and continue parsing, and MUST stop parsing at the End TLV.
3.2 Mandatory TLVs
Chassis ID (type 1) — one subtype byte followed by the identifier. CRDP
emits subtype chassisComponent(1). The value is a stable per-node identifier.
Port ID (type 2) — one subtype byte followed by the identifier. CRDP emits
subtype interfaceName(5); the value is the kernel interface name (for example
eth0).
Time To Live (type 3) — a 2-byte big-endian value in seconds. It tells the
receiver how long the advertisement remains valid. CRDP sets TTL to
3 × beacon_interval (Section 6).
3.3 Optional TLVs
System Name (type 5) — the host name, from gethostname().
System Description (type 6) — a fixed product string (CloudRouter).
System Capabilities (type 7) — two 2-byte fields: supported capabilities and
enabled capabilities. CRDP advertises the router bit (bit 4, value 0x0010)
in both.
Management Address (type 8) — advertises a reachable address for the node. CRDP includes the interface’s link-local IPv6 address first, then up to the cap of global IPv6 addresses. Payload layout follows IEEE 802.1AB §8.5.9:
| Field | Size | Value |
|---|---|---|
mgmt_addr_length | 1 | 1 + address length |
mgmt_addr_subtype | 1 | IANA address family (2 = IPv6, 1 = IPv4) |
mgmt_addr | N | Address octets |
ifnum_subtype | 1 | 2 = ifIndex |
ifnum | 4 | Interface index, big-endian |
oid_len | 1 | 0 (no OID) |
A beacon advertises at most 16 management addresses. A receiver MUST reject a beacon that carries more than 16 (treated as spoofed or misconfigured).
4. Beacon Size
The encoded LLDPDU is bounded so a beacon fits a single unfragmented IPv6 datagram on any conforming link:
- Hard ceiling: 1200 bytes of LLDPDU body. This is budgeted against the IPv6 minimum link MTU of 1280 bytes (RFC 8200) minus the 40-byte IPv6 header, 8-byte UDP header, and 17-byte CRDP obfuscation header, rounded down for headroom.
- On links with a larger MTU the encoder still never exceeds 1200 bytes; on
links with a smaller MTU the budget shrinks to
mtu − 40 − 8 − (17 if obfuscated).
A receiver MUST drop any datagram larger than the maximum obfuscated beacon (1200 + 17 = 1217 bytes) before performing any cryptographic or TLV work. A beacon smaller than 20 bytes cannot hold the mandatory TLVs and MUST be rejected.
5. Protocol Operation
CRDP is a pure announce-and-listen protocol. There is no handshake, no query, and no acknowledgement. Each node periodically multicasts its own state and passively records beacons from others.
sequenceDiagram
participant A as Node A
participant G as Link Multicast Group
participant B as Node B
Note over A,B: Group ff02::ffe:1 — each node beacons independently, no request/response
A->>G: Beacon (chassis=A, port=eth0, ttl=90, addrs)
G->>B: deliver
Note over B: Record neighbour A, expire at now+ttl
B->>G: Beacon (chassis=B, port=eth0, ttl=90, addrs)
G->>A: deliver
Note over A: Record neighbour B, expire at now+ttl
Note over A,B: Repeat every beacon_interval
5.1 Sending
A node emits a beacon on an interface when:
- the periodic timer fires (every
beacon_interval), or - the interface becomes newly reachable (link added, admin-up, or carrier-up), coalesced by a 1-second per-interface debounce.
A node only beacons on an interface that has carrier and is enabled for discovery. The interface MUST have a usable (DAD-completed) link-local address; otherwise the beacon is skipped and retried on the next tick.
5.2 Receiving
On receipt, a node accepts a datagram only if its destination is the CRDP group,
its hop limit is exactly 1, its source address is link-local, its source UDP port
is 4224, and it arrived on an interface joined to the group. It then decodes the
LLDPDU, drops beacons whose Chassis ID matches its own, and records the neighbour
keyed by (local_interface, chassis_id) with an expiry of now + effective_ttl.
Expired neighbours are pruned so the live set reflects current reality.
6. Timers and Liveness
| Timer | Default | Meaning |
|---|---|---|
beacon_interval | 30 s | Cadence of periodic beacons and reconcile |
| Advertised TTL | 90 s | 3 × beacon_interval, sent on the wire |
| Event debounce | 1 s | Coalesces event-driven beacons per interface |
| TTL cap on receive | 5 × beacon_interval (150 s) | Upper bound applied to any peer’s advertised TTL |
Advertising TTL at three times the beacon interval means a neighbour survives two lost beacons before expiring. On the receive side, a peer’s advertised TTL is clamped to five beacon intervals, so a malicious or buggy peer advertising the 2-byte maximum (65535 s ≈ 18 hours) cannot park a stale entry in the table for that long.
7. Optional Obfuscation
When the operator configures a shared secret, beacons are wrapped in a CRDP header and the LLDPDU body is XORed with a keystream. This is obfuscation, not authenticated encryption: there is no MAC. Its purpose is to keep beacons private on shared segments and to make sure only nodes sharing the secret parse each other; it does not provide integrity or authenticity guarantees.
Wire layout:
0 4 5 17 17+N
+-------+--------+-----------+-----------------------+
| "CRDP"| ver=1 | nonce(12) | ciphertext(LLDPDU, N) |
+-------+--------+-----------+-----------------------+
- Magic: 4 bytes, ASCII
CRDP. - Version: 1 byte,
0x01. - Nonce: 12 random bytes, chosen per beacon.
- Ciphertext: the LLDPDU XORed with the keystream.
The keystream is SHA-256 in counter mode. Each 32-byte block is:
block(i) = SHA-256("cloudrouter/crdp/v1" || secret || nonce || counter)
where:
||means byte concatenation."cloudrouter/crdp/v1"is a fixed ASCII context string.secretis the operator’s shared secret;nonceis the 12 header bytes.counteris the block indexi(0, 1, 2, …) written as a big-endian 64-bit unsigned integer — that is, exactly 8 bytes, most-significant byte first.
Block i is XORed against the i-th 32-byte chunk of the LLDPDU.
A node in secret mode accepts only framed beacons; a node in clear-text mode accepts only unframed LLDPDUs. A peer with the wrong secret produces garbage after un-XOR, and the missing mandatory TLVs cause the beacon to be dropped cleanly, without a crypto error.