Packet diagrams
A packet diagram lays out the fields of a network header or a binary record bit by bit, in rows of a fixed width, the way RFCs draw them. Use it to document a protocol header, a file format, a register map, or a message on a wire. It is for anyone who writes or reads code that packs bits, and for an agent that has been asked to explain a format.
TL;DR
| Question | Answer |
|---|---|
| What is it for? | A header or record of fixed-width fields: TCP, UDP, IPv6, a file header, the bits of a hardware register. |
| How do I start one? | Write packet "Title", then one field line for each field with its first and last bit, such as field 0-15 "Source port". |
| How are bits counted? | From 0 at the start of the packet. A row holds 32 bits unless bits= says 8, 16, or 64, and a field that runs past the end of a row carries on in the next. |
| What does Mermaid convert? | packet and packet-beta: every block, by range, by one bit, or by +N. See From Mermaid. |
| How big can it be? | At most 400 fields and 4,096 bits. Warnings start at 64 fields or 16 rows, at bits no field holds, and at a length that is not whole bytes. |
| How is it drawn? | A band for each row with the bit numbers over the first row and each row's bit offset at its left. Each field is a cell with its name across it; a narrow field sets its name on its side, and one too narrow even for that is numbered and named in a key under the rows. |
Ask your agent
Use Plotwork to draw the TCP header as a packet diagram, 32 bits a row. Name every field, colour the SYN, ACK and RST flags, and mark the options as variable length.Use Plotwork to draw the 8-bit status register of our controller as a packet diagram with the most significant bit on the left. Bit 0 is Carry, bit 1 Zero, bit 7 Global interrupt enable.Example
The fixed 20 bytes of a TCP header and its options. The flags are one bit each, so their names are set on their side; the options field has a torn edge because its length varies.
packet "TCP header" desc="The fixed 20 bytes of a TCP segment header and its options, one row per 32-bit word. The flags SYN, ACK, RST and FIN open, confirm, reset and close a connection."
field 0-15 "Source port"field 16-31 "Destination port"field 32-63 "Sequence number"field 64-95 "Acknowledgment number"field 96-99 "Data offset" sub="in 32-bit words"field 100-103 "Reserved"field 104 "CWR"field 105 "ECE"field 106 "URG"field 107 "ACK" tone=greenfield 108 "PSH"field 109 "RST" tone=redfield 110 "SYN" tone=bluefield 111 "FIN"field 112-127 "Window"field 128-143 "Checksum"field 144-159 "Urgent pointer"field 160-191 "Options and padding" variable=onRows of 32 bits, the first bit of each row on the left.
- Source port: bits 0 to 15, 16 bits.
- Destination port: bits 16 to 31, 16 bits.
- Sequence number: bits 32 to 63, 32 bits.
- Acknowledgment number: bits 64 to 95, 32 bits.
- Data offset: bits 96 to 99, 4 bits (in 32-bit words).
- Reserved: bits 100 to 103, 4 bits.
- CWR: bit 104, 1 bit.
- ECE: bit 105, 1 bit.
- URG: bit 106, 1 bit.
- ACK: bit 107, 1 bit.
- PSH: bit 108, 1 bit.
- RST: bit 109, 1 bit.
- SYN: bit 110, 1 bit.
- FIN: bit 111, 1 bit.
- Window: bits 112 to 127, 16 bits.
- Checksum: bits 128 to 143, 16 bits.
- Urgent pointer: bits 144 to 159, 16 bits.
- Options and padding: bits 160 to 191, 32 bits (variable length).
A UDP header with next
next takes the bits right after the field before it, so a format made of whole fields can be listed by width alone.
packet "UDP header" desc="The 8-byte header of a UDP datagram, then its data."next 16 "Source port"next 16 "Destination port"next 16 "Length" sub="header and data, in bytes"next 16 "Checksum" tone=ambernext 32 "Data" variable=onRows of 32 bits, the first bit of each row on the left.
- Source port: bits 0 to 15, 16 bits.
- Destination port: bits 16 to 31, 16 bits.
- Length: bits 32 to 47, 16 bits (header and data, in bytes).
- Checksum: bits 48 to 63, 16 bits.
- Data: bits 64 to 95, 32 bits (variable length).
An 8-bit register
bits=8 makes each bit wide enough for its name, and order=descending puts the most significant bit on the left, as a datasheet does.
packet "Status register" bits=8 order=descending desc="The 8-bit status register of a small microcontroller, most significant bit on the left."field 0 "Carry" tone=bluefield 1 "Zero"field 2 "Negative"field 3 "Overflow" tone=redfield 4 "Sign"field 5 "Half carry"field 6 "Bit copy storage"field 7 "Global interrupt enable" tone=greenRows of 8 bits, the first bit of each row on the right.
- Carry: bit 0, 1 bit.
- Zero: bit 1, 1 bit.
- Negative: bit 2, 1 bit.
- Overflow: bit 3, 1 bit.
- Sign: bit 4, 1 bit.
- Half carry: bit 5, 1 bit.
- Bit copy storage: bit 6, 1 bit.
- Global interrupt enable: bit 7, 1 bit.
The language
The header
packet "Title" starts the diagram. Every option is optional.
| Option | Values | Default | Meaning |
|---|---|---|---|
bits= | 8, 16, 32, 64 | 32 | How many bits a row holds. |
numbers= | on, off | on | The bit numbers over the first row and the bit offset of each row. |
order= | ascending, descending | ascending | descending puts the first bit of each row on the right. |
desc= | text | none | The plain-language description, for assistive technology. |
Statements
| Statement | Meaning | Example |
|---|---|---|
field <first>-<last> "<Name>" | A field from its first bit to its last. Write one number for one bit. | field 0-15 "Source port" |
next <count> "<Name>" | A field of count bits right after the field before it, or from bit 0 for the first. | next 16 "Window" |
desc "<text>" | The plain-language description, the same as desc=. | desc "The UDP header." |
field and next take the same options.
| Option | Values | Meaning |
|---|---|---|
sub= | text | A short note under the name, such as the unit. |
tone= | any tone | The field's colour, for a field that matters: a flag, a checksum, a length. |
variable= | on, off | on draws a field of variable length, such as options or a payload, with a torn edge. |
Fields and bit ranges
Bits are counted from 0 at the start of the packet, not from the start of the row, so the second 32-bit word starts at bit 32. Write the range with a hyphen and no spaces: 96-99. Fields may be written in any order and are drawn in bit order.
Two fields cannot share a bit: the error names the field already there and the bit to start at. Bits that no field holds are drawn hatched and reported by lint, since they are usually a typo; name them "Reserved" when they are reserved.
Bits per row and bit order
A row is 832 pixels shared among its bits: 104 a bit at 8 bits a row, 26 at 32, and 14 at 64. A field that runs past the end of a row is cut there and carries on in the next row, where its name is drawn again in a quieter ink. With order=descending, bit 0 of each row is on the right and the ruler counts down from the left.
Narrow fields
A name is set across its field when it fits in three lines. A field too narrow for that, such as a flag of one bit, sets its name on its side, reading upward, and its row grows to hold it. A name too long even for that, or a narrow field with a note, gets a number in its cell, and the number, the name, the bits and the note are listed in a key under the rows. Text never leaves its field. Short names, such as URG for the urgent pointer flag, keep every name in the rows.
Limits and warnings
| Limit | What happens |
|---|---|
| More than 400 fields, or a bit past 4095 | Refused with E140 before any layout. Draw the payload as one variable field, or split the record. |
| More than 64 fields, or more than 16 rows | W200: split the record by header or part. |
| Bits no field holds | W200 names them. |
| A length that is not a whole number of bytes | W200, unless the last field is of variable length. |
| A name moved to the key | W200 names it, so you can shorten it or use fewer bits a row. |
| A name or note cut to fit | W203. The full text stays in the text description. |
From Mermaid
Mermaid packet and packet-beta convert to a packet diagram. Ask your agent to convert it, or use the convert_mermaid tool. The migration guide covers the converter in full.
packettitle UDP Packet0-15: "Source Port"16-31: "Destination Port"32-47: "Length"48-63: "Checksum"64-95: "Data (variable length)"The converter keeps every block and reports the field it marked variable (M204):
packet "UDP Packet"field 0-15 "Source Port"field 16-31 "Destination Port"field 32-47 "Length"field 48-63 "Checksum"field 64-95 "Data (variable length)" variable=on| Mermaid | Plotwork | Report |
|---|---|---|
packet, packet-beta | packet "<title>" | none |
title X, front matter title: | The header title | none |
0-15: "Name" | field 0-15 "Name" | none |
106: "Name" | field 106 "Name" | none |
+16: "Name" | next 16 "Name" | none |
A name ending in (variable length) | The name, and variable=on | M204 |
| A block that shares bits with an earlier one | Dropped | M209 |
| HTML in a name | Removed | M205 |
| A block with no name | Dropped | M216 |
Front matter config: (bitsPerRow, showBits, bitOrder) and %%{init}%% | Dropped. Set bits=, numbers= and order= on the header. | M200 |
| More than 400 blocks, or a bit past 4095 | Nothing is converted. | M102 |
| A line that cannot be read | Nothing is converted, or the line is skipped when the conversion is lenient. | M110, or M218 |
See also
| Doc | Why |
|---|---|
| Writing diagrams | The rules every kind shares |
| Examples to copy | Finished diagrams with the prompt that gets an agent to write each one |
| Migrating from Mermaid | Convert existing Mermaid diagrams, and what is dropped |
| Matrices | A grid of rows and columns, for a table of values rather than bits |
| Language reference | Every kind, option and diagnostic |
| Diagram kinds | The kinds Plotwork draws and the header of each |