Analyzing the time delta between Profinet packets can provide critical insights into the performance and stability of real-time communication systems. By using Lua scripting in Wireshark, you can automate this analysis, extract detailed timing information between packets, and calculate advanced statistical metrics to assess network behavior.
In this article, we’ll walk through a Lua script that not only calculates the minimum, maximum, and average time deltas between Profinet packets but also computes additional statistics, such as median, variance, standard deviation, and maximum/minimum deviation from the average. These statistics provide a deeper understanding of packet timing variations and network performance.
Table of Contents
Setting Up the Lua Script in Wireshark
Before we dive into the script itself, let’s first ensure that the Lua environment is set up correctly.
Create the Lua Script:
- Open a text editor (e.g., Notepad++, Sublime Text, or Visual Studio Code).
- Save the file with a
.lua
extension. For example, you can name itprofinet_time_delta_analysis.lua
.
Place the Lua Script in the Correct Directory:
Wireshark looks for Lua scripts in specific plugin folders. To find the correct folder:
- Open Wireshark.
- Go to Help > About Wireshark > Folders.
- Locate the “Personal Lua Plugins” folder.
- Move the Lua script (
profinet_time_delta_analysis.lua
) into this folder.
Restart Wireshark:
After placing the Lua script in the correct folder, restart Wireshark so it can load the script. Once the script is placed in the correct folder and Wireshark is restarted, it will automatically load the Lua file, and the analysis will run when you capture or open a Profinet .pcap
file. You can also reload the Lua scripts from within Wireshark by pressing Ctrl+Shift+L.
Code Walkthrough: Function by Function
1. Listener Initialization
We start by creating a listener that captures all Profinet traffic from a specific device.
local profinet_listener = Listener.new("eth", "eth.src == b8:27:eb:f3:9a:b2 and pn_io")
Explanation:
Listener.new()
creates a listener to capture Ethernet traffic.- The filter
eth.src == b8:27:eb:f3:9a:b2
ensures that the script captures traffic only from the specified Profinet device, identified by its MAC address. Replace the sample MAC addressb8:27:eb:f3:9a:b2
in the Lua code with the MAC address of your device. - The
pn_io
filter is used to ensure that only Profinet packets are processed.
2. Field Extraction
Next, we define the field frame.time_delta_displayed
, which extracts the time delta between two consecutive packets.
local time_delta_displayed = Field.new("frame.time_delta_displayed")
Explanation:
Field.new()
defines a field from the packet capture that we want to extract.frame.time_delta_displayed
provides the time difference between the current packet and the previous packet, which is essential for calculating packet timing intervals.
3. Packet Processing
This function handles each packet captured by the listener, skips the first packet (since its time delta is always zero), and updates various statistics such as minimum, maximum, and average time deltas.
local function packet_handler(pinfo, tvb)
local time_delta = time_delta_displayed()
if time_delta then
-- Skip the first packet because its time delta is always 0
if is_first_packet then
is_first_packet = false
return
end
-- Convert time delta to a number
local delta = tonumber(tostring(time_delta))
-- Update statistics
total_time_delta = total_time_delta + delta
packet_count = packet_count + 1
-- Store the time delta for further analysis
table.insert(time_deltas, delta)
-- Set min and max time deltas after the first packet
if not min_time_delta or delta < min_time_delta then
min_time_delta = delta
end
if not max_time_delta or delta > max_time_delta then
max_time_delta = delta
end
end
end
Explanation:
- This function is invoked for every packet that matches the listener’s filter.
- The script skips the first packet because it has no previous packet to compare, resulting in a zero time delta.
- The extracted time delta is added to the
time_deltas
table for further analysis, and the running totals for minimum, maximum, and total time deltas are updated.
4. Additional Statistical Calculations
We include helper functions to calculate additional statistics: the median, variance, and standard deviation.
local function calculate_median(values)
table.sort(values)
local len = #values
if len % 2 == 1 then
return values[(len + 1) // 2]
else
return (values[len // 2] + values[len // 2 + 1]) / 2
end
end
local function calculate_variance_and_stddev(values, mean)
local sum_sq_diff = 0
for _, v in ipairs(values) do
sum_sq_diff = sum_sq_diff + (v - mean) ^ 2
end
local variance = sum_sq_diff / #values
local stddev = math.sqrt(variance)
return variance, stddev
end
Explanation:
- The
calculate_median()
function sorts the time deltas and returns the middle value (or the average of the two middle values if the number of values is even). - The
calculate_variance_and_stddev()
function calculates how far each time delta is from the average and computes the variance and standard deviation. Standard deviation gives a clearer view of how much time deltas vary from the average.
5. Initialization
The reset()
function reinitializes the script’s variables whenever a new capture session starts or Wireshark is reset.
function profinet_listener.reset()
total_time_delta = 0.0
time_deltas = {}
min_time_delta = nil
max_time_delta = nil
packet_count = 0
is_first_packet = true
print("Profinet analysis started")
end
Explanation:
- This function resets all statistics and variables at the start of a new capture.
6. Final Statistics Calculation
Once all packets have been processed, Wireshark calls the draw()
function, which calculates and prints the statistics.
function profinet_listener.draw()
if packet_count > 0 then
-- Calculate average time delta
local avg_time_delta = total_time_delta / packet_count
-- Calculate median time delta
local median_time_delta = calculate_median(time_deltas)
-- Calculate variance and standard deviation
local variance, stddev = calculate_variance_and_stddev(time_deltas, avg_time_delta)
-- Calculate maximum and minimum deviations from average
local max_deviation = nil
local min_deviation = nil
for _, delta in ipairs(time_deltas) do
local deviation = math.abs(delta - avg_time_delta)
if not max_deviation or deviation > max_deviation then
max_deviation = deviation
end
if not min_deviation or deviation < min_deviation then
min_deviation = deviation
end
end
-- Print out the statistics
print("Profinet Time Delta Analysis Completed.")
print("Total Packets: " .. packet_count)
print("Minimum Time Delta: " .. min_time_delta .. " seconds")
print("Maximum Time Delta: " .. max_time_delta .. " seconds")
print("Average Time Delta: " .. avg_time_delta .. " seconds")
print("Median Time Delta: " .. median_time_delta .. " seconds")
print("Variance: " .. variance)
print("Standard Deviation: " .. stddev)
print("Maximum Deviation from Average: " .. max_deviation .. " seconds")
print("Minimum Deviation from Average: " .. min_deviation .. " seconds")
else
print("No packets processed, unable to calculate time delta statistics.")
end
end
Explanation:
This function calculates and prints the key statistics, including:
- Median: The middle value in the sorted time deltas.
- Variance and Standard Deviation: Measures of how much time deltas deviate from the average.
- Maximum and Minimum Deviations from the Average: Shows the largest and smallest deviations from the average time delta.
Complete Lua Script
Below is the full Lua script that provides an extended analysis of the time deltas between Profinet packets:
-- Listener to capture all Profinet traffic from the specified device
local profinet_listener = Listener.new("eth", "eth.src == b8:27:eb:f3:9a:b2 and pn_io")
-- Define the field to extract time delta between packets
local time_delta_displayed = Field.new("frame.time_delta_displayed")
-- Variables to store statistics
local total_time_delta = 0.0
local time_deltas = {} -- Store each time delta for further statistical analysis
local min_time_delta = nil
local max_time_delta = nil
local packet_count = 0
local is_first_packet = true -- Skip the first packet
-- Packet handler function
local function packet_handler(pinfo, tvb)
local time_delta = time_delta_displayed()
if time_delta then
-- Skip the first packet
if is_first_packet then
is_first_packet = false
return
end
local delta = tonumber(tostring(time_delta))
total_time_delta = total_time_delta + delta
packet_count = packet_count + 1
table.insert(time_deltas, delta)
if not min_time_delta or delta < min_time_delta then
min_time_delta = delta
end
if not max_time_delta or delta > max_time_delta then
max_time_delta = delta
end
end
end
-- Helper function to calculate the median
local function calculate_median(values)
table.sort(values)
local len = #values
if len % 2 == 1 then
return values[(len + 1) // 2]
else
return (values[len // 2] + values[len // 2 + 1]) / 2
end
end
-- Helper function to calculate variance and standard deviation
local function calculate_variance_and_stddev(values, mean)
local sum_sq_diff = 0
for _, v in ipairs(values) do
sum_sq_diff = sum_sq_diff + (v - mean) ^ 2
end
local variance = sum_sq_diff / #values
return variance, math.sqrt(variance)
end
-- Reset function
function profinet_listener.reset()
total_time_delta = 0.0
time_deltas = {}
min_time_delta = nil
max_time_delta = nil
packet_count = 0
is_first_packet = true
print("Profinet analysis started")
end
-- Draw function to calculate and print statistics
function profinet_listener.draw()
if packet_count > 0 then
local avg_time_delta = total_time_delta / packet_count
local median_time_delta = calculate_median(time_deltas)
local variance, stddev = calculate_variance_and_stddev(time_deltas, avg_time_delta)
local max_deviation = nil
local min_deviation = nil
for _, delta in ipairs(time_deltas) do
local deviation = math.abs(delta - avg_time_delta)
if not max_deviation or deviation > max_deviation then
max_deviation = deviation
end
if not min_deviation or deviation < min_deviation then
min_deviation = deviation
end
end
print("Profinet Time Delta Analysis Completed.")
print("Total Packets: " .. packet_count)
print("Minimum Time Delta: " .. min_time_delta .. " seconds")
print("Maximum Time Delta: " .. max_time_delta .. " seconds")
print("Average Time Delta: " .. avg_time_delta .. " seconds")
print("Median Time Delta: " .. median_time_delta .. " seconds")
print("Variance: " .. variance)
print("Standard Deviation: " .. stddev)
print("Maximum Deviation from Average: " .. max_deviation .. " seconds")
print("Minimum Deviation from Average: " .. min_deviation .. " seconds")
else
print("No packets processed, unable to calculate time delta statistics.")
end
end
Interpreting the Statistics
Here’s a brief explanation of the key metrics you will see when you run the script:
- Minimum Time Delta: The smallest time difference between two consecutive packets, indicating the fastest packet delivery time.
- Maximum Time Delta: The largest time difference, indicating the slowest packet delivery or largest delay.
- Average Time Delta: The mean time difference between packets, showing the general packet rate.
- Median Time Delta: The middle value of time deltas, showing where most of the packet timing is centered.
- Variance and Standard Deviation: These give insights into how much the packet intervals vary from the average. Low variance and standard deviation indicate more consistent packet timing.
- Maximum and Minimum Deviation from Average: These values show how much some packets deviate from the average packet timing.
Conclusion
This Lua script offers a detailed statistical analysis of Profinet traffic in Wireshark, going beyond simple time delta calculations to provide deeper insights into packet timing. The combination of variance, standard deviation, median, and deviations from the average allows you to diagnose any potential timing issues, ensuring the network performs reliably.
This script is flexible and can be extended further to track additional Profinet-specific metrics, making it a powerful tool for real-time traffic analysis.
Further reading
To deepen your understanding of Profinet and how to optimize its performance using statistical analysis, we recommend exploring the extensive technical documentation available for P-Net. P-Net is a full fledged Profinet stack designed for embedded systems, offering resources for developers looking to implement or enhance Profinet functionality in their projects.
P-Net Technical Documentation: Dive into detailed explanations of the Profinet protocol, implementation guides, and best practices for network optimization.
P-Net GitHub Repository: Access the source code, contribute to the project, and engage with a community of developers working with Profinet.
By exploring these resources, you can gain a deeper insight into Profinet networking, learn how to implement custom solutions, and further optimize your network’s performance using advanced techniques.