— Architecture, Sockets & the MBAP Header

(Module 3 · Modbus TCP/IP — Modbus over Modern Networks)

Mission for this chapter: Show you every byte, flag, and network nuance that turns the classic 1979 PDU into a routable, multiplex-capable, Ethernet-speed superstar—without hiding the sharp edges (Nagle, half-opens, Unit-ID collisions). By the end you’ll be able to design, capture, optimise, and debug Modbus TCP stacks with the same confidence you now have on RS-485.


Chapter map

§TopicWhy you must know it
7.1Why Modbus jumped onto TCP/IPContext & trade-offs
7.2OSI-layer mapping & packet walk-through“Wire to register” end-to-end view
7.3Anatomy of the MBAP headerByte-level Bible for every capture
7.4Transaction lifecycle & socket managementConcurrency, half-open recovery, keep-alive
7.5Performance & determinismNagle, Delayed ACK, segmentation offload
7.6Unit Identifier & gateway multiplexingBridging dozens of RTU loops with one IP
7.7Connection-state pitfallsTIME_WAIT storms, NAT hair-pinning, firewall DPI
7.8Minimal hardening checklistPort isolation ➝ TLS (MBSec) preview
7.9Implementation patterns & codeFull Python, C( libmodbus), async examples
7.10Diagnostics with WiresharkFilters, colouring rules, latency graphs
7.11Best-practice recapOne-page crib-sheet

(Diagram & listing placeholders appear [Fig-7-x] / Listing x.)


7.1 Why Modbus Jumped onto TCP/IP

  1. Routability — RS-485 stops at 1200 m; Ethernet + IP routing reaches another continent.
  2. Bandwidth — 100 Mbit/s Fast Ethernet ≈ 10 000× 9 600 Bd RS-485, enabling multi-MBps historians.
  3. Multi-master freedom — Any PC/HMI may open a socket; no collisions—TCP arbitrates.
  4. Leverage commodity silicon — PHY, MAC, switch = peanuts; no fieldbus ASIC royalties.
  5. Piggy-back on IT infrastructure — One cable carries control, MES, camera feeds, VoIP.

Trade-offs: Loss of implicit determinism (Ethernet CSMA/CD & TCP congestion), infinitely more security exposure, dependence on IPv4/IPv6 address planning.


7.2 Packet Walk-Through — From Wire to Register

[Fig-7-1] Layer “onion” diagram (Ethernet II → IPv4 → TCP → MBAP + PDU).

OSI layerModbus pieceBytesField notes
1/2Ethernet II14 + CRCDest MAC, Src MAC, EtherType 0x0800
3IPv420 (no opts)Src IP, Dst IP, TTL, ID, DF…
4TCP20 (no opts)Src port (=any >1023), Dst port 502
5-6MBAP7Transaction-ID, Protocol-ID, Length, Unit-ID
7PDUnFunction Code + Data

7.3 MBAP Header — The 7-Byte Contract

Byte offsetFieldPurposeTypical value
0-1Transaction IdentifierEchoes; lets client match OOO replies0x0000-0xFFFF
2-3Protocol IdentifierAlways 0x0000 for Modbus0x0000
4-5LengthUnit-ID + PDU byte-count0x0006 (read 3 regs)
6Unit IdentifierSlave-ID behind gateway; 0xFF broadcast0x11

[Fig-7-2] Colour-coded MBAP breakdown with tool-tip call-outs.

Field trivia Protocol-ID ≠ 0 reserved for future multiplex (never standardised). Some vendors hijack 0x000B for legacy registers—block such packets at firewall.


7.4 Transaction Lifecycle & Socket Management

7.4.1 State diagram (client perspective)

CLOSED → TCP_SYN → ESTABLISHED
  ↑       ↓timeout         ↓RST or 5xx
RETRY ←───┴────────── FIN_WAIT → TIME_WAIT → CLOSED

7.4.2 Concurrency patterns

PatternWhen to useCaveat
Single socket, sequential TIDPLC polling one RTU loop via gateway~10 ms turnaround best case
Pooled sockets (N)SCADA station hitting 30+ IP slavesN×TCP buffers – watch RAM
Async multiplex, many outstanding TIDHigh-latency WAN linksMust match replies by TID, not sequence

[Listing 1] Python asyncio + pymodbus snippet opening 8 parallel sockets with independent TIDs.

7.4.3 Idle timeout & keep-alive

  • IEC 61131 PLCs often close socket after 5 s idle (resource thrash).
  • Enable SO_KEEPALIVE (Linux defaults 2 h—raise to 30 s for WAN).
  • If NAT, send zero-length TCP ACK probe every < NAT-timeout (usually 60 s).

7.5 Performance & Determinism

FactorEffectMitigation
Nagle AlgorithmBuffers small frames → adds 40-200 msTCP_NODELAY
Delayed ACKServer waits 40 ms before ACKingCombine with NODELAY; or send 2 frames/RTT
Segmentation Offload (TSO)NIC lumps frames into jumboDisable on low-latency motion links
Switch store-and-forward5–10 µs per hop — negligibleCascade ≤ 3 switches for hard 1 ms loops

[Fig-7-3] Latency vs packet size graph showing nodal contributions.

Throughput formula (single socket, FC03, 125 regs): Tcyc≈(req+resp)×8link Mbit+RTTLANT_\text{cyc} \approx \frac{(\text{req}+ \text{resp}) \times 8}{\text{link Mbit}} + \text{RTT}_{\text{LAN}}

@100 Mbit: 261 B payload → 15 µs wire + 50 µs switch + 50 µs PLC parse ≈ 115 µs—orders faster than RS-485.


7.6 Unit Identifier & Gateway Multiplexing

7.6.1 How a serial-to-TCP gateway routes

TCP 502 (Unit-ID 17)  ─┐
                       ├─>  RS-485 bus #1  → Slave 17
TCP 502 (Unit-ID 05)  ─┤

Gateway prepends Serial Addr = Unit-ID; appends CRC; pushes out.

7.6.2 Common issues

SymptomRoot causeFix
All slaves reply “device busy”Multiple TCP clients hitting same Unit-ID → gateway queuesUse client-side semaphore or dedicate gateway
Wrong slave answersGateway in “transparent” mode ignoring Unit-IDSwitch to “addressed” mode or embed addr in register map
Broadcast failsMany gateways drop Unit-ID 0Use per-slave write loop instead

7.7 Connection-State Pitfalls

  1. TIME_WAIT storms — closing 100 sockets/s on Linux = 100 × 60 s ephemeral hold; tune net.ipv4.tcp_fin_timeout or reuse sockets.
  2. NAT hair-pinning — inside → public IP → same router; older NATs drop port 502 → use local VIP.
  3. Deep Packet Inspection firewalls — will block if Length field doesn’t match actual bytes; verify gateway doesn’t pad payload.

7.8 Minimal Hardening Checklist (pre-MBSec)

LayerDefenceCLI/Setting
NetworkVLAN or VRF isolate port 502switchport access vlan 30
TransportRestrict ingress to whitelisted IPsiptables -p tcp --dport 502 -s 10.0.0.0/24 -j ACCEPT
ApplicationDisable writes on critical slavesGateway ACL / PLC data-diode
Crypto (preview)MBSec (TLS 1.3, X.509, server & client auth)Supported by HMS Anybus SG 4, Schneider M241 fw ≥ 5.3

7.9 Implementation Patterns & Code

7.9.1 Python + pymodbus synchronous example

from pymodbus.client import ModbusTcpClient
cli = ModbusTcpClient('192.168.10.55', port=502)
cli.connect()
for tid in range(1000):
    rr = cli.read_holding_registers(0, 6, unit=0x11)
    assert rr.registers[0] < 4000           # simple range check
cli.close()

7.9.2 C (libmodbus) non-blocking

Listing 2 shows libmodbus + poll() with three concurrent slaves.

7.9.3 Async IO / epoll pattern

  • One socket per slave or one shared socket with Transaction-ID ring.
  • Await reader.readexactly(length) where length = MBAP.len – 1.

7.10 Diagnostics with Wireshark

TaskFilter / toolTip
Isolate Modbus traffictcp.port == 502Colourise foreground green
Show only PDUsmodbus display filterRequires Wireshark dissector ≥ v3
Highlight slow responsesStatistics → I/O Graph → tcp.time_deltaPeaks > 0.1 s = suspect Nagle
Reassemble exceptionFollow → TCP StreamFC+0x80 visible

[Fig-7-4] Screenshot: dual-colour request/response ladder, MBAP decoded in info column.


7.11 Best-Practice Recap

  1. One PDU, seven-byte MBAP = “carry-anywhere” core—learn fields by heart.
  2. Disable Nagle (TCP_NODELAY) when cycle < 100 ms.
  3. Reuse sockets — reconnect storms trigger firewall throttling.
  4. Unit-ID discipline: unique per serial slave behind gateway; 255 = broadcast, seldom supported.
  5. Secure the pipe now—VLAN + ACL at minimum; migrate to MBSec/TLS where vendor offers it.

Visual & downloadable assets to create

IDAssetPurpose
Fig-7-1OSI onion diagramMental model
Fig-7-2Annotated MBAP headerByte cheat-sheet
Fig-7-3Latency vs optimisation graphShow NODELAY effect
Fig-7-4Wireshark ladder captureHands-on reference
Listing 1-3Ready-to-run code filesGit repo / zip download

Up Next

Chapter 8 — Modbus TCP Implementation: Sockets, NAT & Network Nuances will translate today’s theory into hardened network topologies, static vs DHCP, dual-home gateways, VLAN QoS, and cross-protocol comparisons (EtherNet/IP, Profinet). Keep Wireshark open and prepare to tweak firewall rules.

Leave a Reply

Your email address will not be published. Required fields are marked *

Related Posts

Chapter 5 – Modbus RTU

— Efficient Binary Communication (Module 2 · Modbus Serial Protocols — The Original Workhorses) Goal of this chapter Make you 100 % confident with every bit, byte, gap, and CRC…