skip to content

How does a Wireshark display-filter slice such as `eth.src[0:3] == 00:00:83` work, and how do the `[i:j]` and `[i-j]` forms differ?

level: middleimportance: nice to knowfreq 6%

answer

  1. zero-based byte offsets
  2. colon means length
  3. hyphen means inclusive end
  4. negative counts from the end
  5. strings and bytes only

basics

~20 s

A slice selects part of a byte field, string field or protocol by zero-based offset: [i:j] is j units from i, [i-j] is offsets i to j inclusive. eth.src[0:3] is the source MAC's vendor prefix.

solid answer

~40 s

Square brackets after a field or protocol select a slice, counted from zero: `[i:j]` is start and length, `[i-j]` start and inclusive end, `[i]` one element, `[:j]` the first j, `[i:]` from i to the end. So `eth.src[0:3] == 00:00:83` compares the vendor prefix of the source MAC. Offsets can be negative to count from the end: `frame[-4:]` is the last four bytes, which works properly since Wireshark 4.0. Slices apply to byte fields, string fields and protocols, not integers. A byte slice compares with a byte string or with a quoted string, so `tcp.payload[0:4] == "POST"` finds a request-line start on a port no dissector claims. String slices count characters, not bytes.

go deeper

for a junior

Recall that slices are zero-based, that [i:j] is start and length, and that eth.src[0:3] is the vendor prefix of a MAC address.

for a middle

Explain the five slice forms, negative offsets, and how a byte slice compares with hex bytes or a quoted string.

for a senior

Use slices to find traffic the dissectors do not decode, and know that a slice test silently excludes packets too short to contain it.

for a principal

Decide when a fixed-offset slice filter is acceptable evidence and when the protocol should be dissected properly before conclusions are drawn.

## What a slice selects A **slice** takes part of a value, written as square brackets after a field or protocol name. It works when the value is a **byte sequence** (most address fields, payloads, whole protocols) or a **text string**. Integer fields such as `tcp.port` cannot be sliced: compare them as numbers instead. Offsets are **zero-based**. The first byte of `eth.src` is `eth.src[0]`. ## The five forms | Form | Meaning | Example | Selects | |---|---|---|---| | `[i:j]` | start i, length j | `eth.src[0:3]` | bytes 0, 1, 2 | | `[i-j]` | start i, end j inclusive | `eth.src[1-2]` | bytes 1, 2 | | `[i]` | one element at i | `eth.src[2]` | byte 2 | | `[:j]` | first j elements | `eth.src[:4]` | bytes 0-3 | | `[i:]` | from i to the end | `eth.src[4:]` | bytes 4-5 | The classic mix-up is reading `[2:5]` as "bytes 2 to 5". It is five bytes starting at offset 2, that is offsets 2-6; "2 to 5" is `[2-5]`. Ranges can be **concatenated** with commas: `eth.src[0:3,1-2]` joins the first three bytes and bytes 1-2 into one value to compare. ## Negative offsets Negative offsets count from the **end** of the value: `-1` is the last byte. Both of these test the last four bytes of a frame: ``` frame[-4:4] == 00:01:02:03 frame[-4:] == 00:01:02:03 ``` Indexing from the end was a long-standing bug fixed in Wireshark 4.0, so on 4.6 it behaves as documented. ## Comparing slices - A **byte slice** compares with a byte string written as hex separated by colons, dots or hyphens (`00:00:83`), or with a quoted string, which is converted to its UTF-8 bytes. `tcp.payload[0:4] == "POST"` and `tcp.payload[0:4] == 50:4f:53:54` are the same test. - A **string slice** is counted in characters on UTF-8 code-point boundaries, not bytes: `http.content_type[0:4] == "text"` checks the media type's prefix. - A slice can be searched: `frame[100-199] contains "wireshark"`. - A slice can be masked with a bitwise AND, but the mask must be a byte string of the same length: `ip[42:2] & 40:ff`. A slice also implies that the bytes exist. `frame[100-199]` is false on frames shorter than 200 bytes, just as any comparison is false when its field is missing. ## Where slices earn their keep 1. **Vendor prefixes.** `eth.src[0:3] == 00:00:83` keeps frames whose source MAC carries one manufacturer's prefix, useful for finding every frame from one family of appliances on a segment. 2. **Undissected payloads.** When a service on an unusual port shows only as raw TCP data, `tcp.payload[0:4] == "POST"` still finds request starts without telling the dissector anything. 3. **Trailers and magic numbers.** Negative offsets reach fixed trailers, and fixed-offset tests reach signatures at the start of a payload. ## Common mistakes with slices - **Reading the colon as an end offset.** `[2:5]` is five bytes from offset 2; write `[2-5]` for offsets 2 to 5. - **Counting from one.** Offsets start at 0, so the first payload byte is `tcp.payload[0]`. - **Mismatched lengths.** A three-byte slice never equals a four-byte value, so `eth.src[0:3] == 00:00:83:01` matches nothing. - **Slicing a number.** `tcp.port` and other integers cannot be sliced; use comparisons or a bitwise AND on the integer. - **Forgetting short packets.** A slice past the end of a value is false, so frames or payloads too short to hold it silently drop out of the result. - **Bytes versus characters.** On a string field the slice counts characters; to slice the raw bytes of a string, prefix the field with the `@` operator. Most of these produce a filter that compiles and simply matches less than you expected, which is why a slice filter deserves a quick check against one packet you know should match. ## Slices and the layer operator Square brackets after a `#` mean something else: `tcp.port#[2-4]` selects protocol **layers** 2 to 4, not bytes. The `#` is what tells the parser you mean layers; without it, the brackets are a slice.

  • How would you find HTTP POST requests to a service on a port no dissector recognises?
    Slice the TCP payload and compare it with a string: `tcp.payload[0:5] == "POST "`. The quoted string is converted to bytes, and the slice requires at least five payload bytes. It only sees segments that start a request, so a request split oddly across segments can be missed; teaching Wireshark the port is the dissector's business.
  • Why does `tcp.port[0:1] == 01` fail to compile?
    Slices work on byte and string values, and `tcp.port` is an unsigned integer, so it cannot be sliced. Compare it as a number, or use a bitwise mask on the integer: `tcp.port & 0xff00 == 0x0100`.

saying these in an interview costs you the question

  • eth.src[2:5] means bytes 2 through 5 of the address.
  • Slice offsets start at 1, like column numbers in an editor.
  • Any field can be sliced, including integers like tcp.port.
  • A string slice always counts bytes, even for multi-byte characters.
  • tcp.port#[2-4] selects bytes 2 to 4 of the port field.