pcp

package
v0.592.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 7, 2026 License: AGPL-3.0 Imports: 5 Imported by: 0

Documentation

Overview

Package pcp decodes PCP (Port Control Protocol) messages per RFC 6887. PCP is the modern NAT/firewall configuration protocol that supersedes NAT-PMP (RFC 6886) and adds IPv6 support, peer-mapping (for hole-punching), and a more flexible TLV-options envelope. Universal in residential broadband CPE (every ASUS / Netgear / Fritz!Box router / CGNAT enforcement at carriers since ~2014); used directly by uTorrent / qBittorrent / Tailscale's `libpcp` / libnatpmp / miniupnpd to request external port mappings on behalf of inbound-listening applications.

Wrap-vs-native judgement

Native. RFC 6887 is fully public. PCP has a tight
24-byte common header (Version + R-bit + Opcode +
per-direction fields) followed by an opcode-specific
body and an optional TLV options walker. No crypto at
the parse layer.

What this package covers

  • **24-byte common header** (RFC 6887 §7.1):

  • byte 0: Version (must be 2 for PCP).

  • byte 1: R-bit (high bit; 0 = Request from client, 1 = Response from server) + low 7 bits = **Opcode** with **3-entry name table**: 0 ANNOUNCE / 1 MAP / 2 PEER.

  • For **Requests** (R=0):

  • 2-byte Reserved.

  • 4-byte Requested Lifetime (seconds; 0 = delete mapping).

  • 16-byte PCP Client IP Address (IPv4-mapped or IPv6).

  • For **Responses** (R=1):

  • 1-byte Reserved.

  • 1-byte **Result Code** with **14-entry name table** (RFC 6887 §7.4): 0 SUCCESS / 1 UNSUPP_VERSION / 2 NOT_AUTHORIZED / 3 MALFORMED_REQUEST / 4 UNSUPP_OPCODE / 5 UNSUPP_OPTION / 6 MALFORMED_OPTION / 7 NETWORK_FAILURE / 8 NO_RESOURCES / 9 UNSUPP_PROTOCOL / 10 USER_EX_QUOTA / 11 CANNOT_PROVIDE_EXTERNAL / 12 ADDRESS_MISMATCH / 13 EXCESSIVE_REMOTE_PEERS.

  • 4-byte Lifetime (granted; or error retry-after on negative Result Code).

  • 4-byte Epoch Time (server's monotonic re-anchor counter).

  • 12-byte Reserved.

  • **MAP opcode body** (Opcode 1; RFC 6887 §11):

  • 12-byte Mapping Nonce (client-generated cookie for request/response correlation).

  • 1-byte Protocol (IP proto number; 0 = "all protocols").

  • 3-byte Reserved.

  • 2-byte Internal Port (the port the client wants to receive on).

  • 2-byte Suggested External Port (client hint; server may pick differently).

  • 16-byte Suggested External IP Address (client hint; server may pick differently).

  • **PEER opcode body** (Opcode 2; RFC 6887 §12) — same as MAP plus a remote-peer tuple appended:

  • 2-byte Remote Peer Port.

  • 2-byte Reserved.

  • 16-byte Remote Peer IP Address.

  • **ANNOUNCE opcode** (Opcode 0) — no opcode-specific body; the common header alone signals server epoch reset (clients must refresh all mappings when received).

  • **Options walker** (RFC 6887 §7.3) — optional TLV records appended after the opcode body. Each option:

  • 1-byte Option Code (high bit = mandatory).

  • 1-byte Reserved.

  • 2-byte Option Length (uint16 BE; excludes header).

  • Padded to 4-byte boundary. **6-entry option code name table** (RFC 6887 + 7488

  • 6970 + 7843): 1 THIRD_PARTY (request mapping on behalf of another IP) / 2 PREFER_FAILURE (don't downgrade to a different port if requested unavailable) / 3 FILTER (restrict the mapping to a specific peer) / 4 NAT64_PREFIX (DS-Lite / NAT64 prefix discovery) / 5 PORT_SET (request multiple consecutive ports as one mapping).

What this package does NOT cover (deliberately out of scope)

  • UDP framing — feed PCP bytes after the UDP header strip. PCP runs on UDP destination port 5351 (server side); clients listen on UDP 5350 for ANNOUNCE multicasts.

  • NAT-PMP (RFC 6886) — the predecessor protocol that PCP supersedes (8-byte messages, IPv4-only, simpler). Could share most decoder logic but has a different envelope; deferred.

  • PCP Authentication (RFC 7652) — optional auth extension via 2 additional option types (5 ASMAP_CAPA, 6 NONCE) — surfaced as raw hex via the generic option walker.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type MapBody

type MapBody struct {
	MappingNonceHex          string `json:"mapping_nonce_hex"`
	Protocol                 int    `json:"protocol"`
	ProtocolName             string `json:"protocol_name"`
	InternalPort             int    `json:"internal_port"`
	SuggestedExternalPort    int    `json:"suggested_external_port"`
	SuggestedExternalAddress string `json:"suggested_external_address"`
}

MapBody is the decoded body of a MAP-opcode message.

type Option

type Option struct {
	Code      int    `json:"code"`
	CodeName  string `json:"code_name"`
	Mandatory bool   `json:"mandatory"`
	Length    int    `json:"length"`
	ValueHex  string `json:"value_hex,omitempty"`
}

Option is one TLV record from the options walker.

type PeerBody

type PeerBody struct {
	MapBody
	RemotePeerPort    int    `json:"remote_peer_port"`
	RemotePeerAddress string `json:"remote_peer_address"`
}

PeerBody is the decoded body of a PEER-opcode message.

type RequestHeader

type RequestHeader struct {
	RequestedLifetimeSec uint32 `json:"requested_lifetime_seconds"`
	ClientIPAddress      string `json:"pcp_client_ip_address"`
}

RequestHeader is the request-specific portion of the common header (R-bit = 0).

type ResponseHeader

type ResponseHeader struct {
	ResultCode     int    `json:"result_code"`
	ResultCodeName string `json:"result_code_name"`
	LifetimeSec    uint32 `json:"lifetime_seconds"`
	EpochTime      uint32 `json:"epoch_time"`
}

ResponseHeader is the response-specific portion of the common header (R-bit = 1).

type Result

type Result struct {
	Version    int    `json:"version"`
	IsResponse bool   `json:"is_response"`
	Opcode     int    `json:"opcode"`
	OpcodeName string `json:"opcode_name"`
	TotalBytes int    `json:"total_bytes"`

	// Per-direction header fields.
	RequestHeader  *RequestHeader  `json:"request_header,omitempty"`
	ResponseHeader *ResponseHeader `json:"response_header,omitempty"`

	// Per-opcode bodies.
	MapBody  *MapBody  `json:"map_body,omitempty"`
	PeerBody *PeerBody `json:"peer_body,omitempty"`

	Options []Option `json:"options,omitempty"`
	Notes   []string `json:"notes,omitempty"`
}

Result is the top-level decoded view of a PCP message.

func Decode

func Decode(hexStr string) (*Result, error)

Decode parses a single PCP message from hex.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL