There doesn't seem to be a protocol spec for Modbus UDP on the Modbus site:
Specifications and Implementation Guides
Modbus Technical Resources - other sites lists several libraries that have implemented Modbus UDP. They are all different languages and it hasn't been verified if they all agree on what the UDP data should look like.
Thank you for providing the sample captures.
At this point it's worth opening a Wireshark enhancement request to add this to the Modbus UDP dissector.
It is a bit more than I want to tackle and also not sure about corner cases like trailer bytes after the Modus data or if the data is large and is split into IP fragments.
Enhancement requests are added as a Feature Request in the Wireshark Issues on GitLab.
Until it can be added properly, here is a lua script to decode UDP data containing multiple ADUs.
No warranty - Caveat emptor - but seems to work with the sample UDP capture provided.
-- Modbus_ADUs.lua
-- https://ask.wireshark.org/question/37817/modbus-udp-versus-tcp/
--
-- Step 1 - document as you go. See header above and set_plugin_info().
local modbus_adus_info =
{
version = "1.0.0",
author = "Good Coder",
description = "Modbus UDP - multiple ADUs",
repository = "Floppy in top drawer"
}
set_plugin_info(modbus_adus_info)
-- grab the original dissector to call from our loop below
local mbudp_dissector = Dissector.get("mbudp")
-- Step 2 - create a protocol to attach new fields to
local modbus_adus_p = Proto.new("modbus_adus","Modbus/UDP/ADUs")
-- Steps 3 and 4 deleted for readability
-- See https://wiki.wireshark.org/lua/ for original EASYPOST.lua with all steps
-- Step 5 - create the dissector function that will run on each frame/packet
function modbus_adus_p.dissector(tvb,pinfo,tree)
local subtree = nil
local tvb_len =tvb:captured_len()
local offset = 0
if not subtree then
subtree = tree:add(modbus_adus_p)
end
while offset < tvb_len do
new_tvb = tvb:range(offset):tvb()
mbudp_len = new_tvb:range(4,2):int() + 6
mbudp_dissector:call(new_tvb, pinfo, tree)
offset = offset + mbudp_len
end
end
-- Step 6 - replace original mbudp dissector with modbus_adus dissector
local udp_port_table = DissectorTable.get("udp.port")
udp_port_table:set(502, modbus_adus_p)
Frame 2: Packet, 504 bytes on wire (4032 bits), 504 bytes captured (4032 bits) on interface xxx
Ethernet II, Src: xxx, Dst: xxx
Internet Protocol Version 4, Src: xxx (xxx), Dst: xxx (xxx)
User Datagram Protocol, Src Port: 502, Dst Port: 56014
Modbus/UDP/ADUs
Modbus/UDP
Modbus
Modbus/UDP
Modbus
Modbus/UDP
Modbus
Modbus/UDP
Modbus
Can you provide a sample capture with multiple queries in a UDP frame?
Any links to a standard or RFC that support this?
epan/dissectors/packet-mbtcp.c:
dissect_mbudp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) { if (!is_mbtcp(tvb, pinfo)) return 0; /* Make entries in Protocol column on summary display */ col_set_str(pinfo->cinfo, COL_PROTOCOL, "Modbus/UDP"); col_clear(pinfo->cinfo, COL_INFO); return dissect_mbtcp_pdu_common(tvb, pinfo, tree, proto_mbudp, global_mbus_udp_ports); }Looks like the code just does the first PDU for UDP.
I tried to copy capture file in here, but looks like I need more points to do that. I can copy the bytes from the message here, if that helps.
The format follows the Modbus Messaging on TCP/IP Implementation Guide V1.0b from october 2006, chapter 4.2.1.1, and rule number 4. It uses the transaction identifier to relate queries and responses.
Here is the hex dump of the bytes from the UDP request containing the 4 queries in one UDP telegram. The transaction identifiers are 0,1,2 and 3, to identify each query. I have marked each transaction identifier in bold.
0000 00 a0 1d 2c b7 98 00 a0 1d 13 b0 1f 08 00 45 00 0010 00 4c b8 50 40 00 40 11 f6 f4 c0 a8 85 09 c0 a8 0020 85 01 da ce 01 f6 00 38 91 ...(more)
(The comment above had reached max comment size so reposting as two comments)
I tried to copy capture file in here, but looks like I need more points to do that. I can copy the bytes from the message here, if that helps.
The format follows the Modbus Messaging on TCP/IP Implementation Guide V1.0b from october 2006, chapter 4.2.1.1, and rule number 4. It uses the transaction identifier to relate queries and responses.
Here is the hex dump of the bytes from the UDP request containing the 4 queries in one UDP telegram. The transaction identifiers are 0,1,2 and 3, to identify each query. I have marked each transaction identifier in bold.
(more)(Repost second section. This hex dump may be truncated due to max comment size above?)
(more)The response hex dump is also packed into one UDP telegram as follows, and I have marked the related transaction identifiers in bold also here.
The capture file can be placed on a public file share then the question updated with a link to it.