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:

  1. fields - An array of field names to extract from packets
  2. 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:

  1. A field name string (e.g., '%s' or 'dns.qry.name')
  2. 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}`;
    }
  }
);