profinetdcp

package
v0.331.0 Latest Latest
Warning

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

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

Documentation

Overview

Package profinetdcp decodes Profinet DCP (Discovery and Configuration Protocol) frames per IEC 61158-6-10. DCP is the **bootstrap** protocol for Profinet networks — the first protocol an attacker tapping into a Siemens-shop factory floor sees when enumerating devices, and the protocol Profinet IO controllers use at boot time to find their distributed I/O devices and assign them station names + IP addresses.

DCP runs **directly over Ethernet** (EtherType 0x8892) — no IP, no UDP — so it is a Layer 2-only protocol bounded to a single broadcast domain. Operationally, DCP carries:

  • **Identify** — multicast "who's there?" used by Siemens TIA Portal, Step7, and Profinet IO controllers at engineering time to enumerate every Profinet-capable device on a wire. Each device responds with its station name, vendor, device ID, role, and current IP parameters.
  • **Set** — unicast "set the station name + IP" commands sent by an engineering station or IO controller to bring a fresh device into a known configuration.
  • **Get** — unicast attribute query (read the station name, read the IP, read the device options).
  • **Hello** — multicast announcement an IO device sends after reboot to declare its presence to controllers.

Wrap-vs-native judgement

Native. IEC 61158-6-10 + the Profinet wiki + Wireshark's pn_dcp
dissector fully specify the wire format. DCP frames are a
tight FrameID + 10-byte fixed header followed by a TLV block
stream with Option/Suboption discriminator bytes. No crypto
at the parse layer.

What this package covers

  • **FrameID** (2 bytes, big-endian; transmitted immediately after the 0x8892 EtherType):

  • 0xFEFE: **DCP Hello** (multicast announcement)

  • 0xFEFD: **DCP Get/Set** (unicast request/response)

  • 0xFEFC: **DCP Identify Request** (multicast)

  • 0xFEFB: **DCP Identify Response** (multicast)

  • **DCP header** (10 bytes, big-endian):

  • byte 0: **ServiceID** — 0x03 Get / 0x04 Set / 0x05 Identify / 0x06 Hello.

  • byte 1: **ServiceType** — 0x00 Request / 0x01 Response Success / 0x05 Response Not Supported.

  • bytes 2-5: **Xid** (uint32 BE; transaction identifier for request/response pairing).

  • bytes 6-7: **ResponseDelay** (uint16 BE; on Identify requests, the receiver waits up to this many 10-ms ticks × DCP_TICK_FACTOR before replying to spread the response storm).

  • bytes 8-9: **DCPDataLength** (uint16 BE; bytes of TLV blocks following).

  • **TLV block walker** — each block is laid out as:

  • byte 0: **Option** (1 byte; categorical bucket).

  • byte 1: **Suboption** (1 byte; per-Option child).

  • bytes 2-3: **DCPBlockLength** (uint16 BE; bytes of payload following — INCLUDING the 2-byte BlockInfo for response blocks).

  • bytes 4+: payload (per-Option/Suboption shape).

  • Inter-block padding: blocks are 16-bit-aligned; odd-length blocks get a 1-byte 0x00 pad.

  • **7-entry Option name table**: 0x01 `IP` (subopts: MAC / IP_Parameter / Full_IP_Suite) / 0x02 `DeviceProperties` (subopts: Vendor / NameOfStation / DeviceID / DeviceRole / DeviceOptions / AliasName / DeviceInstance / OEMDeviceID) / 0x03 `DHCP` / 0x04 `LLDP` / 0x05 `ControlBlock` (subopts: Start / Stop / Signal / Response / FactoryReset / ResetToFactory) / 0x06 `DeviceInitiative` / 0xFF `AllSelector`.

  • **Per-Option/Suboption decoder set** (high-runners):

  • **IP / MAC** (0x01 / 0x01): 6-byte MAC address.

  • **IP / IP_Parameter** (0x01 / 0x02): IPv4 Address + Subnet Mask + Gateway (each 4 bytes BE).

  • **IP / Full_IP_Suite** (0x01 / 0x03): IP_Parameter

  • 4 DNS server addresses.

  • **DeviceProperties / Vendor** (0x02 / 0x01): manufacturer name VISIBLE-STRING.

  • **DeviceProperties / NameOfStation** (0x02 / 0x02): station name VISIBLE-STRING (the unique IO-device identifier used by the IO controller — e.g. "et200sp.field-01").

  • **DeviceProperties / DeviceID** (0x02 / 0x03): VendorID (uint16 BE) + DeviceID (uint16 BE).

  • **DeviceProperties / DeviceRole** (0x02 / 0x04): bitmask — bit 0 IO-Device, bit 1 IO-Controller, bit 2 IO-Multidevice, bit 3 PN-Supervisor.

  • **DeviceProperties / DeviceOptions** (0x02 / 0x05): list of (Option, Suboption) pairs the device supports.

  • **DeviceProperties / DeviceInstance** (0x02 / 0x07): 2-byte high + 2-byte low instance identifier.

  • **DeviceProperties / OEMDeviceID** (0x02 / 0x08): OEM vendor + device IDs (same shape as DeviceID).

  • **ControlBlock / Signal** (0x05 / 0x03): "flash LED on the target" — the bench-engineering primitive for identifying a device by sight on the rack.

  • **AllSelector / All** (0xFF / 0xFF): request every option in a single round trip (the standard IdentifyAll body).

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

  • **L2 framing** — feed Profinet DCP bytes after the 14-byte Ethernet header (destination MAC, source MAC, EtherType 0x8892). Standard DCP destination is multicast group 01:0E:CF:00:00:00 for Identify / Hello, or unicast for Get/Set targeted at a specific device. VLAN tagging via IEEE 802.1Q with PCP=6 priority is common but is part of the L2 frame and not parsed here.
  • **Other Profinet FrameID ranges** — RT cyclic I/O data (FrameID 0x8000-0xBFFF), PTCP timing (0xFF40-0xFF43), Acyclic RT (0xFE00-0xFEFA) — different frame shapes that require their own decoders. This package specifically targets the DCP range (0xFEFB-0xFEFE).
  • **BlockInfo field** — response blocks carry a 2-byte BlockInfo at the start of the payload (BlockQualifier + Status); surfaced as raw payload bytes for the per-Option decoders to peek at, but not separately parsed.
  • **Profinet IO state-machine** — connection establishment (CR / AR setup), I/O exchange, alarm framing — higher- level analysis driven by GSD file metadata.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Block

type Block struct {
	Option        int    `json:"option"`
	OptionName    string `json:"option_name"`
	Suboption     int    `json:"suboption"`
	SuboptionName string `json:"suboption_name"`
	Length        int    `json:"length"`
	PayloadHex    string `json:"payload_hex,omitempty"`

	// Per-option decoded fields (only one set populated).
	MAC               string `json:"mac,omitempty"`
	IPAddress         string `json:"ip_address,omitempty"`
	SubnetMask        string `json:"subnet_mask,omitempty"`
	Gateway           string `json:"gateway,omitempty"`
	Vendor            string `json:"vendor,omitempty"`
	NameOfStation     string `json:"name_of_station,omitempty"`
	VendorID          int    `json:"vendor_id,omitempty"`
	DeviceID          int    `json:"device_id,omitempty"`
	DeviceRoleHex     string `json:"device_role_hex,omitempty"`
	DeviceRoleDecoded string `json:"device_role_decoded,omitempty"`
}

Block is one TLV record in the DCP body.

type Result

type Result struct {
	TotalBytes int `json:"total_bytes"`

	// FrameID + name
	FrameID     int    `json:"frame_id"`
	FrameIDName string `json:"frame_id_name"`

	// DCP header (10 bytes)
	ServiceID       int    `json:"service_id"`
	ServiceIDName   string `json:"service_id_name"`
	ServiceType     int    `json:"service_type"`
	ServiceTypeName string `json:"service_type_name"`
	Xid             uint32 `json:"xid"`
	ResponseDelay   int    `json:"response_delay"`
	DCPDataLength   int    `json:"dcp_data_length"`

	// TLV blocks
	Blocks []Block `json:"blocks,omitempty"`
}

Result is the structured decode of a Profinet DCP frame.

func Decode

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

Decode parses a Profinet DCP frame from a hex string starting at the FrameID bytes (i.e. AFTER the 14-byte Ethernet header + 0x8892 EtherType strip). Separators (':' '-' '_' whitespace) are tolerated; a leading '0x' prefix is stripped.

Jump to

Keyboard shortcuts

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