Skip to main content Skip to sidebar

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.

ParameterValue
Address familyIPv6
Multicast groupff02::ffe:1 (link-local scope)
UDP port4224 (source and destination)
Hop limit1
ScopeSingle 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:

TypeNamePresence
0End of LLDPDUMandatory (terminator)
1Chassis IDMandatory
2Port IDMandatory
3Time To LiveMandatory
5System NameOptional
6System DescriptionOptional
7System CapabilitiesOptional
8Management AddressOptional, repeatable
127Organizationally SpecificIgnored 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:

FieldSizeValue
mgmt_addr_length11 + address length
mgmt_addr_subtype1IANA address family (2 = IPv6, 1 = IPv4)
mgmt_addrNAddress octets
ifnum_subtype12 = ifIndex
ifnum4Interface index, big-endian
oid_len10 (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

TimerDefaultMeaning
beacon_interval30 sCadence of periodic beacons and reconcile
Advertised TTL90 s3 × beacon_interval, sent on the wire
Event debounce1 sCoalesces event-driven beacons per interface
TTL cap on receive5 × 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.
  • secret is the operator’s shared secret; nonce is the 12 header bytes.
  • counter is the block index i (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.