EtherNet/IP Encapsulation Protocol Explained: The 24-Byte Header That Carries CIP

By | June 16, 2026

Open Wireshark on an EtherNet/IP network and the first thing you see is not CIP. You see TCP. Then a strange 24-byte header. Then more bytes inside that look like CIP. The 24-byte header is the EtherNet/IP Encapsulation Protocol — the layer that wraps every CIP message onto a TCP/IP network. Nobody talks about it because everyone calls it “EtherNet/IP” and moves on. But understanding the encapsulation layer is the difference between troubleshooting at the cable level and troubleshooting at the application level.

This article unpacks the encapsulation protocol from the official ODVA specification: the 24-byte header byte by byte, the 10 commands, how sessions work, the Common Packet Format that organizes connected and unconnected data, and the status codes the layer returns. For broader CIP context, see our CIP Protocol Complete Guide.

What the encapsulation protocol is in one paragraph

The EtherNet/IP Encapsulation Protocol is the transport adaptation layer that lets CIP run over standard TCP/IP networks. It defines a fixed 24-byte header that sits between the TCP or UDP payload and the actual CIP message inside. Every device on EtherNet/IP listens on TCP port 0xAF12 (44818) for connection-oriented traffic and UDP port 0xAF12 (44818) for connectionless commands. The protocol provides session management for TCP, identity discovery, service enumeration, and the framing needed to know where each encapsulated message begins and ends. CIP itself does not know or care that encapsulation is happening — it sees only its own messages flow through.

Note: TCP/UDP port 44818 is the encapsulation port. UDP port 2222 — which often comes up alongside — is used for actual I/O messaging traffic, not for encapsulation commands. The two ports serve different purposes on the same protocol.

Where the encapsulation layer sits in the stack

Take a CIP message that needs to travel over Ethernet. Here is what it looks like on the wire from outer to inner:

| Ethernet | IP | TCP/UDP | Encapsulation Header (24 bytes) | Command-Specific Data |
                                                                       |
                                                              (may contain CIP)

Layer by layer:

LayerWhat it doesBytes
EthernetPhysical addressing, EtherType14 bytes
IPNetwork routing20 bytes
TCP or UDPTransport delivery20 bytes (TCP) / 8 bytes (UDP)
Encapsulation headerEtherNet/IP framing24 bytes fixed
Command-specific dataThe CIP message (when applicable)Variable, up to 65,511 bytes

The encapsulation header is the same 24 bytes for every message — whether you are reading an Identity Object, opening a CIP connection, or sending a 16-byte I/O update. The command field at the start tells the receiver what comes next.

The 24-byte encapsulation header

Every encapsulated message starts with this fixed structure from the ODVA spec (Edition 1.4, Table 2-3.1):

OffsetSizeFieldTypePurpose
02 bytesCommandUINTWhich encapsulation operation this is
22 bytesLengthUINTBytes following the header (0 to 65,511)
44 bytesSession HandleUDINTTarget-assigned session ID (TCP)
84 bytesStatusUDINTResult code (0 = success on replies; 0 on requests)
128 bytesSender ContextARRAY[8]Sender-defined value, echoed back unchanged in replies
204 bytesOptionsUDINTMust be 0 in current spec

Total: 24 bytes. Always 24 bytes, regardless of command.

After the header comes the optional command-specific data — variable length from 0 to 65,511 bytes. For some commands this is empty. For others it carries the entire CIP request or response.

Byte order — the endianness gotcha

From the ODVA spec, section 2-3.1:

“Multi-byte integer fields in the encapsulation messages shall be transmitted in little-endian byte order. NOTE: This is different from the byte ordering used in standard Internet network protocols, which is big-endian.”

So the encapsulation header is little-endian. Standard TCP, UDP, IP, and Ethernet headers around it are big-endian. Some embedded structures inside the payload (like Sockaddr Info, which we cover below) are explicitly big-endian.

This trips up developers writing their own EtherNet/IP parsers. If you read the Command field as 0x6500 instead of 0x0065, you are reading big-endian when you should be little-endian.

Sender Context — the request matching mechanism

The Sender Context is 8 bytes you put into the request and get back unchanged in the reply. The spec says you can put any value there. Most implementations use it as a transaction ID — write your own counter into it, match the reply by reading the same bytes back. This is how clients pipeline multiple unconnected requests without losing track of which reply belongs to which request.

The 10 encapsulation commands

From spec Table 2-3.2, here are the command codes and which transport each can use:

CodeCommandTransportPurpose
0x0000NOPTCP onlyKeep-alive / connection probe. No reply.
0x0004ListServicesTCP or UDPAsk the target which encapsulation service classes it supports.
0x0063ListIdentityTCP or UDPAsk the target to identify itself. Usually broadcast UDP for discovery.
0x0064ListInterfacesTCP or UDPOptional. List non-CIP communication interfaces.
0x0065RegisterSessionTCP onlyStart a session. Get back a Session Handle.
0x0066UnRegisterSessionTCP onlyEnd a session. No reply.
0x006FSendRRDataTCP onlySend an unconnected (UCMM) CIP request/reply through a session.
0x0070SendUnitDataTCP onlySend a connected CIP message through an open CIP connection. No reply at encapsulation level.
0x0072IndicateStatusTCP onlyOptional. Target indicates status to originator.
0x0073CancelTCP onlyOptional. Cancel a previously-sent command.

A device that receives a command it does not support must return a status code (0x0001) without breaking the session.

How a session actually works

Sessions exist only for TCP. UDP messages (ListIdentity, ListServices) need no session — they are fire-and-forget.

Establishing a session

The spec (section 2-5.2) defines the sequence:

  1. TCP connect to port 44818 on the target.
  2. Send RegisterSession (command 0x0065). The command-specific data is just 4 bytes: Protocol Version (UINT = 1) and Options Flags (UINT = 0).
  3. Target checks protocol version. If supported, the target generates a unique Session Handle. If not, status 0x69 (unsupported protocol revision) is returned.
  4. Target replies with RegisterSession. Status 0 = success. The Session Handle field of the reply contains the target-assigned identifier.
  5. Originator saves the Session Handle. Every subsequent encapsulation command on this TCP connection must carry the same Session Handle.

After step 5 the session is open and the originator can send SendRRData (unconnected CIP) or SendUnitData (connected CIP) messages.

Terminating a session

Two ways, per spec section 2-5.3:

  • UnRegisterSession (command 0x0066) — the polite way. Sender issues this, receiver closes the TCP connection. No reply.
  • Just close TCP — works but the spec calls UnRegisterSession the “preferred” approach because it results in more timely cleanup.

If the TCP connection breaks for any reason (cable pull, host crash, firewall timeout), the session is terminated automatically. Both sides clean up their state.

Maintaining a session

The session stays open as long as the underlying TCP connection is alive. There is no encapsulation-level heartbeat. The spec mentions that TCP keep-alive can be used to detect dead connections that have been idle for long periods, but TCP retry/timeout typically takes several minutes to declare a connection dead.

In practice, clients send periodic NOP messages or read trivial attributes to keep the session warm and detect failures early.

ListIdentity — how every EtherNet/IP discovery tool works

This is one of the most commonly used encapsulation commands. RSLinx, BootP/DHCP tools, network scanners, your own custom tools — they all use ListIdentity to find devices on the network.

The mechanics are clean:

  1. Send a UDP broadcast to port 44818 with command 0x0063 and an empty body.
  2. Every EtherNet/IP device on the broadcast domain replies with a ListIdentity response containing its Identity Object data.

The reply contains a Common Packet Format with one Identity Item (type code 0x000C). From spec Table 2-4.4, the Identity Item carries:

FieldTypeDescription
Encapsulation Protocol VersionUINTAlways 1 currently
Socket AddressSTRUCT (big-endian)sin_family (2 = AF_INET), sin_port, sin_addr, 8 zero bytes
Vendor IDUINTManufacturer (e.g., 1 = Rockwell, 26 = Beckhoff)
Device TypeUINTProfile code (e.g., 0x0C = Communications Adapter, 0x07 = General Purpose Discrete I/O)
Product CodeUINTVendor-defined product identifier
RevisionUSINT[2]Major and minor firmware revision
StatusWORDIdentity Object’s Status attribute
Serial NumberUDINTDevice serial number
Product NameSHORT_STRINGHuman-readable name
StateUSINTIdentity Object’s State attribute

This is what RSLinx shows you in its network browse view. The list of devices, their vendor names, product types, firmware versions, IP addresses — all of it comes from ListIdentity replies.

Note the mixed endianness inside the payload: the encapsulation header is little-endian, but the Socket Address struct (sin_family, sin_port, sin_addr, sin_zero) is explicitly big-endian. This matches the Winsock specification convention.

SendRRData and SendUnitData — where CIP actually rides

These are the two commands that carry CIP traffic.

SendRRData (0x006F) — unconnected messaging

Used for UCMM (Unconnected Message Manager) traffic — request/response style. Each message stands alone. No CIP connection has to be opened in advance.

The command-specific data after the encapsulation header:

FieldTypeValue
Interface HandleUDINTMust be 0 for CIP
TimeoutUINTSeconds to wait (0 = use CIP’s own timeout)
Encapsulated PacketCommon Packet FormatThe CIP message

Inside the Common Packet Format, you have a Null Address Item (type 0x0000, no data) followed by an Unconnected Data Item (type 0x00B2) containing the actual CIP request.

This is how you read someone’s IP address via the TCP/IP Interface Object. How you fetch an Identity Object attribute. How RSLinx browses tag lists. UCMM via SendRRData is the workhorse of CIP configuration traffic.

SendUnitData (0x0070) — connected messaging

Used for traffic on an already-open CIP connection. The Connection ID identifies which CIP connection this message belongs to. There is no reply at the encapsulation level — the CIP connection itself handles request/response semantics.

The command-specific data:

FieldTypeValue
Interface HandleUDINT0
TimeoutUINT0 (CIP handles its own timeout)
Encapsulated PacketCommon Packet FormatConnected Address Item + Connected Data Item

The Common Packet Format here contains a Connected Address Item (type 0x00A1, carrying the Connection ID) and a Connected Data Item (type 0x00B1, carrying the CIP transport packet).

The Common Packet Format

The CPF is how encapsulation organizes payloads that may contain multiple parts. From spec Table 2-6.1:

FieldTypeDescription
Item CountUINTNumber of items in this packet (at least 2)
Address ItemItem StructWhere this packet is going
Data ItemItem StructThe actual encapsulated data
Optional additional itemsItem StructExtra items (e.g., Sockaddr Info)

Every item has the same wrapper:

FieldTypeDescription
Type IDUINTWhat kind of item this is
LengthUINTBytes of data that follow
DataVariableThe item content

The Type IDs that matter, from spec Table 2-6.3:

Type IDKindUsed for
0x0000AddressNull address — used for unconnected messages with no routing needed
0x000CDataListIdentity response item
0x00A1AddressConnection-based address — carries the Connection ID for connected messages
0x00B1DataConnected Transport packet — the CIP connected message
0x00B2DataUnconnected message — the CIP unconnected message
0x0100DataListServices response item
0x8000DataSockaddr Info, originator-to-target
0x8001DataSockaddr Info, target-to-originator
0x8002AddressSequenced Address item — carries Connection ID + Sequence Number for Class 0/1 I/O

Sequenced Address Item — used for I/O messaging

The Sequenced Address item (0x8002) is what UDP-based I/O messaging on port 2222 uses. From spec Table 2-6.6:

FieldTypeValue
Type IDUINT0x8002
LengthUINT8
Connection IdentifierUDINTThe Connection ID assigned in Forward_Open
Sequence NumberUDINTMonotonic counter — detects out-of-order or dropped packets

This is the address item your PLC reads when it receives I/O data from a remote drop. The Connection ID identifies which Forward_Open this packet belongs to. The Sequence Number lets the receiver detect dropped or duplicated cycles.

Sockaddr Info Item — the connection bootstrap

When opening a CIP connection (Forward_Open), the originator may include Sockaddr Info items in the request to tell the target where to send the UDP I/O packets. From spec Table 2-6.9:

FieldTypeBig-endian?Value
Type IDUINTNo0x8000 (O→T) or 0x8001 (T→O)
LengthUINTNo16
sin_familyINTYes2 (AF_INET)
sin_portUINTYesUDP port (typically 2222 for I/O)
sin_addrUDINTYesIP address
sin_zeroUSINT[8]YesAll zeros

This is patterned after the Winsock sockaddr_in structure — explaining the big-endian convention even though the rest of the encapsulation is little-endian. Network programmers will recognize it immediately.

Encapsulation status codes

These appear in the Status field of a reply when something went wrong at the encapsulation layer (not the CIP layer). From spec Table 2-3.3:

CodeMeaningWhat it usually indicates
0x0000SuccessThe command was processed
0x0001Invalid or unsupported encapsulation commandReceiver got a Command field it does not recognize
0x0002Insufficient memoryReceiver could not allocate encapsulation-layer buffers
0x0003Poorly formed dataLength field doesn’t match, malformed payload
0x0064Invalid session handleUsed a stale or wrong Session Handle
0x0065Invalid lengthLength field is invalid for the command
0x0069Unsupported encapsulation protocol revisionRegisterSession with a Protocol Version the target does not support

These are encapsulation-level errors. Application-level CIP errors (object not found, attribute not allowed, etc.) come back inside the CIP payload, not at this layer. If you see encapsulation status 0x0064 in a SendRRData reply, the session expired or you got the Session Handle wrong — it has nothing to do with the CIP service inside.

A complete flow — what happens when RSLinx reads a tag

Walk through the encapsulation traffic for a typical operation: RSLinx reading the Product Name attribute from a ControlLogix PLC at 192.168.1.10.

T = 0  Client → TCP SYN  →  PLC : 44818
T = 1  PLC → TCP SYN-ACK  →  Client
T = 2  Client → TCP ACK  →  PLC
       [TCP connection established]

T = 3  Client → RegisterSession (TCP)
       Command: 0x0065, Length: 4
       Session Handle: 0, Status: 0
       Sender Context: [arbitrary 8 bytes]
       Options: 0
       Data: Protocol Version=1, Options Flags=0

T = 4  PLC → RegisterSession reply (TCP)
       Command: 0x0065, Length: 4
       Session Handle: 0x12345678 (target-assigned)
       Status: 0 (success)
       Sender Context: [echoed from request]
       Data: Protocol Version=1, Options Flags=0

T = 5  Client → SendRRData (TCP)
       Command: 0x006F, Length: ~30 bytes
       Session Handle: 0x12345678
       Status: 0
       Sender Context: [transaction ID]
       Data: 
         Interface Handle: 0
         Timeout: 5 seconds
         CPF:
           Item Count: 2
           Null Address Item (type 0x0000, length 0)
           Unconnected Data Item (type 0x00B2, length ~20)
             CIP Request:
               Service: 0x0E (Get_Attribute_Single)
               Path: Class 0x01, Instance 1, Attribute 7

T = 6  PLC → SendRRData reply (TCP)
       Command: 0x006F, Status: 0
       Session Handle: 0x12345678
       Sender Context: [echoed]
       Data:
         CPF:
           Null Address Item
           Unconnected Data Item:
             CIP Reply: General Status 0 (success), Data: "1756-L73"

T = 7  Client → UnRegisterSession (TCP)
       Command: 0x0066, Length: 0
       Session Handle: 0x12345678

T = 8  TCP FIN/ACK  [connection closed]

Every line on this trace is visible in Wireshark with the EtherNet/IP and CIP dissectors. Once you can read this, you can troubleshoot anything happening at the encapsulation layer.

For more on the CIP layer itself, see our CIP Object Model article.

Capturing in Wireshark

Wireshark has dissectors for both Encapsulation (ENIP) and CIP. The filters you actually need:

enip                              # All encapsulation traffic
enip.command == 0x0065            # Only RegisterSession (find session starts)
enip.command == 0x0066            # Only UnRegisterSession
enip.command == 0x0063            # Only ListIdentity (find device discovery)
enip.command == 0x006F            # Only SendRRData (UCMM traffic)
enip.command == 0x0070            # Only SendUnitData (connected I/O over TCP)
enip.status != 0                  # Any encapsulation-level error
tcp.port == 44818                 # All EtherNet/IP TCP traffic
udp.port == 44818                 # All encapsulation UDP (discovery)
udp.port == 2222                  # All I/O messaging UDP (not encapsulation port)

The encapsulation status field is particularly useful when something is failing intermittently. Filter on enip.status != 0 to find every encapsulation-level error in a long capture without scrolling through thousands of frames.

For broader EtherNet/IP capture workflows, see our Wireshark for EtherNet/IP guide.

Common encapsulation-layer problems

Most “EtherNet/IP not working” tickets are really encapsulation problems misdiagnosed as CIP issues.

SymptomLikely causeWhat to check
TCP connects but nothing else worksSession never registered, or wrong Session HandleCapture and look for the RegisterSession reply — status must be 0
Connection works briefly, then errors with status 0x0064Session expired or device rebootedRe-establish with a new RegisterSession
ListIdentity returns no devicesUDP broadcast blocked by switch or firewallVerify port 44818 UDP is reachable on the broadcast domain
Status 0x0069 on RegisterSessionDevice does not support requested protocol versionSpec defines version 1 currently — almost always supported
RSLinx browse shows partial devicesSome devices respond too slowly to broadcast, get filteredIncrease RSLinx browse timeout
Custom application parses commands wrongEndianness assumption mismatchEncapsulation header is little-endian; Sockaddr fields are big-endian
I/O connection opens but no data flowsUDP port 2222 blockedDifferent port from encapsulation — port 2222 is the I/O messaging port
Wireshark shows command 0x0070 with errors but no replySendUnitData generates no reply at encapsulation layerCheck the underlying CIP connection state, not the encapsulation layer

What this article does not cover

The encapsulation protocol is one piece of EtherNet/IP. Several related topics live in separate chapters of the ODVA specification and warrant their own articles:

  • Mapping I/O messaging to TCP/IP — How Connection IDs, multicast groups, and UDP port 2222 actually carry cyclic I/O data. Defined in Chapter 3 of the spec.
  • CIP Object Model — How the data inside SendRRData and SendUnitData is structured. Covered in our CIP Object Model article.
  • EDS files — How devices describe their objects and assemblies. Defined in Chapter 7 of the spec.
  • Device profiles — How vendor-agnostic devices like AC Drives and Discrete I/O standardize their object instances.
  • Bridging and routing — How CIP messages cross from EtherNet/IP to DeviceNet or ControlNet. Defined in Chapter 10.

Frequently asked questions

What is the EtherNet/IP Encapsulation Protocol?

The Encapsulation Protocol is the transport adaptation layer that wraps CIP messages onto TCP/IP. It defines a fixed 24-byte header used by every EtherNet/IP message, regardless of whether the message carries an identity query, a configuration read, or cyclic I/O data. The protocol is defined in Chapter 2 of ODVA’s CIP Networks Library Volume 2.

What port does the Encapsulation Protocol use?

TCP port 0xAF12 (decimal 44818) for connection-oriented traffic and UDP port 0xAF12 (decimal 44818) for connectionless commands like ListIdentity. UDP port 2222 is also part of EtherNet/IP but carries I/O messaging (defined in Chapter 3 of the spec), not encapsulation commands.

How big is the EtherNet/IP encapsulation header?

The header is exactly 24 bytes, always. It contains the Command field (2 bytes), Length field (2 bytes), Session Handle (4 bytes), Status (4 bytes), Sender Context (8 bytes), and Options (4 bytes). After the header comes optional command-specific data of up to 65,511 bytes.

Is the encapsulation header big-endian or little-endian?

Little-endian. This is unusual — most Internet protocols use big-endian (network byte order). The spec calls this out explicitly. Some embedded structures inside the payload (like Sockaddr Info) are big-endian to match Winsock conventions. Mixing the two is a common source of bugs in custom EtherNet/IP code.

What is the Session Handle?

The Session Handle is a 4-byte identifier the target assigns during a successful RegisterSession command. Every subsequent encapsulation command on the same TCP connection must carry this handle. If you use an invalid or stale handle, the target returns encapsulation status 0x0064 (Invalid Session Handle).

What is the difference between SendRRData and SendUnitData?

SendRRData (0x006F) carries unconnected CIP messages — request/response style traffic using the UCMM (Unconnected Message Manager). SendUnitData (0x0070) carries connected CIP messages over an already-open CIP connection identified by a Connection ID. SendRRData generates a reply at the encapsulation level; SendUnitData does not.

What is ListIdentity used for?

ListIdentity (command 0x0063) is the discovery mechanism. A client sends it as a UDP broadcast to port 44818, and every EtherNet/IP device on the broadcast domain replies with its Identity Object information (Vendor ID, Product Name, Serial Number, IP address, firmware revision, etc.). This is how tools like RSLinx populate their network browse view.

What is the Common Packet Format?

The Common Packet Format (CPF) is an extensible item-based container used inside encapsulation command payloads. It has an Item Count followed by an Address Item, a Data Item, and optional additional items. Each item has a Type ID, Length, and Data. The CPF is what carries CIP requests, replies, and connection data inside the encapsulation wrapper.

Do I need a session for ListIdentity?

No. ListIdentity is a UDP command — no TCP connection and no session are needed. RegisterSession and UnRegisterSession only apply to TCP. UDP-based encapsulation commands (ListIdentity, ListServices via UDP, ListInterfaces via UDP) are completely sessionless.

What is the Sender Context field for?

The Sender Context is 8 bytes you put into a request and get back unchanged in the reply. The spec leaves the value up to the sender. Most clients use it as a transaction ID to match replies with requests — write your own counter into it, match the reply by comparing the same 8 bytes. This lets clients pipeline multiple requests through the same session.

What is encapsulation status code 0x0069?

Status 0x0069 means “unsupported encapsulation protocol revision.” It appears when a RegisterSession requests a Protocol Version the target does not support. The current and only defined version is 1, so this code is rarely seen in production — but if you see it, the target will return the highest supported version in its reply.

Are encapsulation status codes the same as CIP General Status codes?

No. Encapsulation status codes (0x0000 through 0x0069 per spec Table 2-3.3) indicate errors at the encapsulation layer — wrong command, invalid session, malformed data. CIP General Status codes (a different set, defined in the CIP Common Specification) indicate errors at the application layer — object not found, attribute not allowed, service not supported. An encapsulation success (status 0) can still wrap a CIP failure inside, and vice versa.

Author: Zakaria El Intissar

I'm an automation and industrial computing engineer with 12 years of experience in power system automation, SCADA communication protocols, and electrical protection. I build tools and write guides for Modbus, DNP3, IEC 101/103/104, and IEC 61850 on ScadaProtocols.com to help engineers decode, analyze, and troubleshoot real industrial communication systems.