PEX.md 3.9 KB

PEX - Peer Endpoint eXchange protocol

Header:

struct pex_hdr {
    uint8_t version;
    uint8_t opcode;
    uint16_t len;
    uint8_t id[8];
};
  • version: always 0 for now
  • opcode: message type
  • len: payload length
  • id: local peer id

All multi-byte integer fields are in big-endian byte order. Peer identifiers contain the first 8 bytes of the public key

Message types

opcode=0: PEX_MSG_HELLO

Payload (single item):

struct pex_hello {
    uint16_t flags;
    uint8_t local_addr[16];
};
  • local_addr: Local IPv4 or IPv6 address used for connecting to the remote endpoint
  • flags: Bit 0: local_addr is an IPv6 address

Sent after any successful handshake.

opcode=1: PEX_MSG_NOTIFY_PEERS

Used to send information about one or more peers, either proactively, or as a response to PEX_MSG_QUERY

Payload (multiple):

struct pex_peer_endpoint {
    uint16_t flags;
    uint16_t port;
    uint8_t peer_id[PEX_ID_LEN];
    uint8_t addr[16];
};
  • port: endpoint port
  • addr: IPv4 or IPv6 endpoint address
  • peer_id: peer ID
  • flags: Bit 0: addr is an IPv6 address Bit 1: addr refers to the local network address of the peer

opcode=2: PEX_MSG_QUERY

Used to ask for the endpoint address of one or more peers. Expects a PEX_MSG_NOTIFY_PEERS response, but only if there is known data about any of the queried peers.

Payload (multiple):

uint8_t peer_id[8];

For any peer in the payload list that has a known endpoint address, compare the IP address against the endpoint address of the sender of this message. If the IP address matches, send back the local address of the peer (from the PEX_MSG_HELLO message) instead of the discovered wireguard endpoint address. This helps with establishing a direct connection through double-NAT.

opcode=3: PEX_MSG_PING

Used to ping a peer (to keep the connection alive). No payload.

opcode=4: PEX_MSG_PONG

Response to PEX_MSG_PING. No payload.

Unencrypted messages (outside of the tunnel)

These are only supported for networks using signed network data that can be updated dynamically. The struct pex_hdr header is followed by a second header:

struct pex_ext_hdr {
    uint64_t nonce;
    uint8_t auth_id[8];
};
  • nonce: nonce for id hash
  • auth_id: first 8 bytes of the auth public key

In these messages, pex_hdr::id is XORed with siphash(req_id || req_id, auth_key)

opcode=5: PEX_MSG_UPDATE_REQUEST

This message can be used outside of the wireguard tunnel in order to request signed network data It is used to ask a peer for the latest signed network data

Payload:

struct pex_update_request {
    uint64_t cur_version;
    uint32_t req_id;
};
  • cur_version: latest version of the network data that the sender already has
  • req_id: request id copied to response messages

opcode=6: PEX_MSG_UPDATE_RESPONSE

Used to send updated signed network data to a peer

Payload:

struct pex_update_response {
    uint64_t req_id;
    uint32_t data_len;
    uint8_t e_key[32];
};

followed by the first chunk of network data.

  • req_id: request id of the PEX_MSG_UPDATE_REQUEST message
  • data_len: total length of the network data
  • e_key: ephemeral curve25519 public key

The network data is chacha20 encrypted with the following key:

DH(e_key_priv, peer_key)

And using req_id as nonce.

  • e_key_priv: private key belonging to e_key
  • peer_key: public key belonging to the receiver (from the network data)

opcode=7: PEX_MSG_UPDATE_RESPONSE_DATA

Continuation of PEX_MSG_UPDATE_RESPONSE network data

Payload:

struct pex_update_response_data {
    uint64_t req_id;
    uint32_t offset;
};

followed by encrypted network data

opcode=8: PEX_MSG_UPDATE_RESPONSE_NO_DATA

Indicates that the network data with the timestamp given in PEX_MSG_UPDATE_REQUEST is up to date

Payload:

struct pex_update_response_no_data {
    uint64_t req_id;
    uint64_t cur_version;
};
  • req_id: request id of the PEX_MSG_UPDATE_REQUEST message
  • cur_version: latest version of the network data