Custom Ladder Diagrams
The Packet Viewer library provides the defineLadderDiagram()
function to create
custom configurations for ladder diagram visualizations. This function allows you
to define how packet data is extracted and displayed in sequence diagrams, with
type-safe field access and automatic fallback to sensible defaults.
Overview
Ladder diagrams (sequence diagrams) visualize the flow of network communication
between endpoints over time. By default, the ladder diagram shows source and
destination endpoints with a label formatted as {protocol}: {info}
. The
defineLadderDiagram()
function allows you to customize this behavior to display
specific packet fields that are relevant to your analysis.
Basic Usage
The defineLadderDiagram()
function is available in the @qacafe/pv-react/components
module:
import { defineLadderDiagram } from "@qacafe/pv-react/components";
The function takes two parameters:
- fields - An array of field names to extract from packets
- config - An optional configuration object defining how to extract ladder diagram data
The simplest usage creates a ladder diagram with default settings:
import { PacketViewerProvider, LadderDiagram } from "@qacafe/pv-react";
import { defineLadderDiagram } from "@qacafe/pv-react/components";
import "@qacafe/pv-react/style.css";
const ladderConfig = defineLadderDiagram([]);
function MyLadderView() {
return (
<PacketViewerProvider pcap="capture.pcap" endpoint="/api">
<LadderDiagram config={ladderConfig} />
</PacketViewerProvider>
);
}
This creates a ladder diagram that displays:
- Source and destination endpoints
- Protocol and info as the label
- Timestamps with packet numbers on the left
Field Names
Field names can be either column format specifiers (like %s
, %d
, %p
) or
display filter field names (like tcp.flags.syn
, http.request.method
).
Common Column Format Specifiers:
%s
- Source address%d
- Destination address%p
- Protocol name%i
- Info/summary text%t
- Timestamp (specified by the profile)%Rt
- Relative timestamp from the start of the file%hs
- Hardware source address (MAC)%hd
- Hardware destination address (MAC)
For all available column formats, run tshark -G column-formats
. For display
filter fields, see the
Wireshark Display Filter Reference.
Configuration Options
The configuration object supports the following properties. All properties are optional and fall back to sensible defaults:
Property | Description | Default Value |
---|---|---|
source |
Defines the source node | '%s' (source address) |
dest |
Defines the destination node | '%d' (destination address) |
label |
Defines the text displayed on the arrow between endpoints | (p) => \ ${p.fields[’%p’]}: ${p.fields[’%i’]}`` |
time |
Defines the absolute timestamp display on the left | '%t' (profile-defined timestamp) |
relTime |
Used internally to compute delta-time values | '%Rt' (relative timestamp from start of file) |
getChildren |
Optional function to render additional content below each packet | (none) |
Important: The relTime
property is used internally by the ladder diagram
component to compute delta-time values and should not be redefined. Modifying
this property may cause unexpected behavior in the diagram’s time-based features.
Each property (except getChildren
) accepts a FieldAccessor, which can be either:
- A field name string (e.g.,
'%s'
or'dns.qry.name'
) - A function that extracts a value from a packet
Custom Field Access
Using Field Names
The simplest approach is to map configuration properties to different field names:
const config = defineLadderDiagram(['%hs', '%hd'], {
source: '%hs', // Use hardware (MAC) source address
dest: '%hd', // Use hardware (MAC) destination address
// Other properties use defaults
});
Note: Default fields (%s
, %d
, %i
, %p
, %t
, %Rt
) are automatically
included at runtime. Only add them to the fields array if you’re using them in
your configuration functions.
Using Custom Functions
For more complex formatting, provide a function that extracts data from the packet:
const config = defineLadderDiagram([
'tcp.flags.syn', 'tcp.flags.ack', 'tcp.flags.fin', 'tcp.flags.reset', '%p', '%i'
], {
label: (packet) => {
const protocol = packet.fields['%p'];
const info = packet.fields['%i'];
// Show TCP flags in a compact format
if (protocol === 'TCP') {
const flags = [];
if (packet.fields['tcp.flags.syn'] === 'Set') flags.push('SYN');
if (packet.fields['tcp.flags.ack'] === 'Set') flags.push('ACK');
if (packet.fields['tcp.flags.fin'] === 'Set') flags.push('FIN');
if (packet.fields['tcp.flags.reset'] === 'Set') flags.push('RST');
return flags.length > 0 ? flags.join('/') : info;
}
return `${protocol}: ${info}`;
}
});
The function receives a packet object with a fields
property containing all
requested fields.
Type Safety
The defineLadderDiagram()
function is fully typed with TypeScript. When you
specify fields in the array, TypeScript will:
- Provide autocomplete for field names in your accessor functions
- Ensure type safety when accessing
packet.fields
- Validate that your configuration is correctly structured
Important: For TypeScript to provide autocomplete and type safety, any field
you access via packet.fields['fieldname']
must be declared in the fields array
passed to defineLadderDiagram()
. Even though default fields are automatically
included at runtime, TypeScript needs them explicitly declared in the array to
know about them.
Examples
MAC Address Endpoints
Show hardware addresses instead of IP addresses:
const macLadder = defineLadderDiagram(['%hs', '%hd'], {
source: '%hs',
dest: '%hd',
});
HTTP-Specific Ladder Diagram
Display HTTP method and URI in the label:
const httpLadder = defineLadderDiagram(
['http.request.method', 'http.request.uri', 'http.response.code', '%p', '%i'],
{
label: (packet) => {
const method = packet.fields['http.request.method'];
const uri = packet.fields['http.request.uri'];
const code = packet.fields['http.response.code'];
if (method && uri) {
return `${method} ${uri}`;
} else if (code) {
return `Response: ${code}`;
}
return `${packet.fields['%p']}: ${packet.fields['%i']}`;
}
}
);
DNS Query/Response Ladder
Customize the label to show DNS query names and response codes:
const dnsLadder = defineLadderDiagram(
['dns.qry.name', 'dns.flags.response', 'dns.flags.rcode'],
{
label: (packet) => {
const queryName = packet.fields['dns.qry.name'];
const isResponse = packet.fields['dns.flags.response'];
const rcode = packet.fields['dns.flags.rcode'];
if (isResponse === 'Message is a response') {
return `DNS Response: ${queryName} (${rcode})`;
}
return `DNS Query: ${queryName}`;
}
}
);