In Wireshark 4.6, why does `tcp.port in {443, 4430..4434}` match fewer packets than `tcp.port == 443 || (tcp.port >= 4430 && tcp.port <= 4434)`?
answer
- two ports per packet
- each comparison picks its own port
- in tests one value against the set
- commas, and .. for ranges
basics
~20 sThe in operator tests each tcp.port value against the whole set, ranges included. The spelled-out filter tests >= and <= separately, and a packet from port 56789 to port 80 satisfies each with a different port.
solid answer
~40 s`tcp.port` occurs twice per segment, once for each end, and every ordinary comparison is satisfied when any occurrence matches. In `tcp.port >= 4430 && tcp.port <= 4434` the two comparisons are evaluated independently, so a segment from 56789 to 80 passes: 56789 is at least 4430 and 80 is at most 4434. The membership operator `in` instead asks whether a single occurrence lies in the set, so `4430..4434` behaves as a real range. In Wireshark 4.6 set elements are comma-separated, ranges use `..`, and sets can hold strings, addresses or times: `http.request.method in {"HEAD", "GET"}`, `ip.addr in {192.0.2.5 .. 192.0.2.9}`. Whitespace-only separators were deprecated in 3.6 and are a syntax error since 4.0.
go deeper
Recall the in operator with comma-separated values and the .. range syntax, and that sets can hold strings and addresses as well as numbers.
Explain why a range built from two comparisons over a two-valued field such as tcp.port matches extra packets, and why in does not.
Review filters inherited from older runbooks for whitespace-separated sets and comparison-pair ranges before relying on them during an incident.
Decide whether a team's shared filter library should standardise on in for ranges, and how a filter review would catch multi-value pitfalls.
## The membership operator `in` checks a field against a **set** written in curly braces. `tcp.port in {80, 443, 8080}` is the short form of `tcp.port == 80 || tcp.port == 443 || tcp.port == 8080`, and for single values the two are equivalent. Sets are not limited to numbers: - strings: `http.request.method in {"HEAD", "GET"}` - address ranges: `ip.addr in {192.0.2.5 .. 192.0.2.9, 198.51.100.1..198.51.100.9}` - time offsets: `frame.time_delta in {10 .. 10.5}` - mixed values and ranges: `tcp.port in {443, 4430..4434}` A range is written with `..` between its two inclusive ends, and spaces around it do not matter. ## Why the spelled-out range is wider The difference comes from **multi-value fields**. `tcp.port` is added twice to every TCP segment, once for the source port and once for the destination port. An ordinary comparison such as `tcp.port >= 4430` is true when *any* occurrence satisfies it. When you build a range from two comparisons joined by `&&`, each comparison is free to pick a different occurrence. Take a client request from port 56789 to a web server on port 80: | Test | Occurrence that satisfies it | Result | |---|---|---| | `tcp.port >= 4430` | 56789 | true | | `tcp.port <= 4434` | 80 | true | | `tcp.port >= 4430 && tcp.port <= 4434` | different ports | true | | `tcp.port in {4430..4434}` | none lies in 4430-4434 | false | The membership test asks whether one occurrence lies inside the range, which is what you meant. So on any field that can repeat, write ranges with `in` rather than with a pair of comparisons. ## Syntax that changed across releases 1. **Wireshark 3.6** required commas between set elements and deprecated the old whitespace separator, so `{"GET" "HEAD"}` had to become `{"GET", "HEAD"}`. It also added `a not in b`, meaning `not a in b`. 2. **Wireshark 4.0** made whitespace-only separators a **syntax error**. 3. **Wireshark 4.6**, the current release, documents comma-separated elements with `..` ranges; a set copied from a note written for 3.4 or earlier turns the filter bar red until the commas are added. ## Curly braces mean two things The filter language also uses curly braces to group **arithmetic**, because parentheses do not work there: `tcp.dstport >= 4 * {tcp.srcport + 3}`. A brace after `in` is a set; a brace inside an arithmetic expression is grouping. The manual warns not to confuse the two. ## Using sets on a web-tier capture When a web tier listens on several ports, sets keep filters readable: - `tcp.port in {80, 443, 8080, 8443}` to keep every front-end and back-end hop. - `http.request.method in {"POST", "PUT", "DELETE"}` to see only requests that change state. - `http.response.code in {502, 503, 504}` to separate gateway and availability failures from application 500s. - `ip.addr in {192.0.2.20 .. 192.0.2.29}` to keep one pool of back ends. Negating a set needs the same care as negating `==`. `tcp.port not in {80, 443}` hides a segment if either port is in the set, and, like every comparison, it is false on packets that have no TCP ports at all. Write `not tcp.port in {80, 443}` when frames without the field should stay visible. ## Replacing a long or-chain Incident filters tend to grow by accretion: `tcp.port == 80 or tcp.port == 8080 or tcp.port == 8443 or tcp.port == 443`. Rewriting it as `tcp.port in {80, 443, 8080, 8443}` changes nothing about which packets match, because a set of single values is exactly an any-equal test against each element. What it changes is readability: a reviewer sees the list at once, adding a port is one edit, and a typo in one of four repeated field names can no longer hide in the middle of the chain. The gain in meaning only appears with **ranges**, where `in` keeps both bounds on the same occurrence. ## What to check when a set filter surprises you - Are the elements separated by commas, not just spaces? - Does the field repeat inside one packet, and does the filter mean any occurrence or every occurrence? - Are string elements double-quoted? - Is a brace being read as a set when you meant arithmetic grouping?
- How would you keep only the HTTP responses that signal a gateway or availability failure?Use a set of status codes: `http.response.code in {502, 503, 504}`. It reads better than three `==` tests joined by `or`, and for a set of single values the two forms are equivalent. Use `http.response.code >= 500` when every server error counts.
- Why does Wireshark 4.6 reject `http.request.method in {"GET" "HEAD"}`?Set elements must be separated by commas. Whitespace-only separators were deprecated in 3.6 and became a syntax error in 4.0, so the filter must be written `http.request.method in {"GET", "HEAD"}`.
saying these in an interview costs you the question
- tcp.port >= 4430 && tcp.port <= 4434 tests one port against the range.
- Set elements can be separated by spaces, as in {"GET" "HEAD"}.
- The in operator only accepts integers, never strings or addresses.
- A range inside a set excludes its upper bound, like a slice length.
- Parentheses and curly braces are interchangeable for grouping arithmetic.