skip to content

To hide a noisy health checker at 192.0.2.10 in Wireshark 4.6, what does `ip.addr != 192.0.2.10` match, and when are `!==` or `===` right instead?

level: seniorimportance: should knowfreq 22%

answer

  1. a field can occur twice
  2. != is all-not-equal since 3.6
  3. implicit existence test
  4. !== any, === all, from 4.0
  5. any and all quantifiers

basics

~20 s

Since Wireshark 3.6, != means all-not-equal: ip.addr != 192.0.2.10 keeps IPv4 packets where neither address is that host, and hides frames with no ip.addr at all. !== (any not equal) and === (all equal) arrived in 4.0.

solid answer

~40 s

`ip.addr` matches both the source and the destination, so it occurs at least twice per IPv4 packet. `==` is any-equal; since 3.6, `!=` is all-not-equal, the negation of `==`, so `ip.addr != 192.0.2.10` hides every packet to or from the checker. Before 3.6 it meant any-not-equal and matched almost everything, which is why older advice says `!(ip.addr == 192.0.2.10)`. One difference remains: `!=` still carries an implicit existence test, so ARP and IPv6 frames, which have no `ip.addr`, vanish too; `!(ip.addr == 192.0.2.10)` keeps them. Wireshark 4.0 added `===` (`all_eq`) and `!==` (`any_ne`) for fields that repeat inside one packet, such as `dns.a`, plus the `any` and `all` quantifiers; the old `~=` was deprecated then and the 4.6 parser rejects it.

go deeper

for a junior

Recall that ip.addr matches source or destination and that != hides every packet touching the address in current Wireshark releases.

for a middle

Explain field occurrences and the four operators ==, !=, === and !==, with one example where === and == give different answers.

for a senior

Show you can predict a negated filter on a mixed capture, including frames that lack the field, and translate filters from pre-3.6 runbooks without changing their meaning.

for a principal

Treat shared filters as code: decide who reviews the team's saved filters after a release that changes operator meaning, and how the change is announced.

## Why one field can have several values A display-filter field is not a single variable. Each time a dissector adds it to the protocol tree, the packet gains another **occurrence**. `ip.addr` is added once for the source and once for the destination, so every IPv4 packet carries at least two. An ICMP error that quotes the original IP header carries more, and `count(ip.addr) > 2` finds those. Repeating fields are common: `tcp.port`, every A record in a DNS answer (`dns.a`), every header of a repeated type. A comparison on a multi-value field therefore needs a rule for combining occurrences. That rule is exactly what changed across releases. ## The four equality operators in Wireshark 4.6 | Operator | Aliases | True when | Example | |---|---|---|---| | `==` | `eq`, `any_eq` | any occurrence equals | `ip.addr == 192.0.2.10` | | `!=` | `ne`, `all_ne` | no occurrence equals (all not equal) | `ip.addr != 192.0.2.10` | | `===` | `all_eq` | every occurrence equals | `dns.a === 192.0.2.10` | | `!==` | `any_ne` | at least one occurrence differs | `dns.a !== 192.0.2.10` | So, to hide the health checker, `ip.addr != 192.0.2.10` works: a packet from the checker has one occurrence equal to it, so not all occurrences differ, and the packet is hidden. ## What changed in 3.6 and 4.0 - **Before 3.6**, `!=` meant *any not equal*. A packet from 192.0.2.10 to 198.51.100.20 has one address that differs, so `ip.addr != 192.0.2.10` matched it, and the filter showed almost everything. The folk fix was `!(ip.addr == 192.0.2.10)`, and it is still in many write-ups. - **3.6** made `a != b` mean `!(a == b)`, removing the contradiction where `a == b` and `a != b` were both true. It offered `~=` and `any_ne` to get the old behaviour. - **4.0** added `===` (`all_eq`) and `!==` (`any_ne`), the aliases `any_eq` and `all_ne`, and the quantifiers `any` and `all`, which work with any relation: `all tcp.port > 1024` is true only when both ports are above 1024. It deprecated `~=` in favour of `!==`. - **4.6** no longer has a `~=` token, so a filter copied from an old note fails to compile. ## The existence test that still differs Every comparison on a field first needs the field to be present; on a packet without it, the comparison is false. That is why `!=` and `!(...==...)` are not quite twins: - `ip.addr != 192.0.2.10` is false on ARP, on IPv6 and on any other frame without an IPv4 address, so they disappear. - `!(ip.addr == 192.0.2.10)` negates a false comparison on those frames, so they stay. On a capture where the checker is the only noise and you still want to see neighbour discovery or IPv6 traffic, the second form is the honest one. The `ip.addr` field covers IPv4 only; IPv6 has its own fields. ## When `===`, `!==` and the quantifiers earn their place 1. A DNS response can carry several A records. `dns.a === 192.0.2.10` keeps answers whose every A record is that address; `dns.a !== 192.0.2.10` keeps answers with at least one other address, a quick way to spot a name whose answer set is mixed. 2. `all tcp.port > 1024` keeps only segments whose source and destination ports are both above 1024. 3. With tunnelled traffic, the **layer operator** narrows the test instead: `ip.addr#1 == 192.0.2.10` looks only at the outer IP header and `ip.addr#2` at the inner one. ## Translating filters from older runbooks | Filter in an older note | What it meant then | Wireshark 4.6 form with the same meaning | |---|---|---| | `ip.addr != 192.0.2.10` (before 3.6) | any address differs | `ip.addr !== 192.0.2.10` | | `!(ip.addr == 192.0.2.10)` | no address equals | unchanged | | `ip.addr ~= 192.0.2.10` (added in 3.6, deprecated in 4.0) | any address differs | `ip.addr !== 192.0.2.10` | In practice the first row was almost always a bug in the old note, so translate the intent rather than the operator: the author nearly always wanted the host gone. ## Reading a filter someone hands you Before trusting a negated filter on a busy capture, ask three things: does the field repeat in one packet, which of the four operators is written, and should frames without the field stay visible? Those three answers, not the shape of the expression, decide what the packet list shows.

  • A filter from an old runbook, `ip.addr ~= 192.0.2.10`, turns the 4.6 filter bar red. What do you write instead?
    `~=` meant any-not-equal; 4.0 deprecated it in favour of `!==`, and the 4.6 parser no longer accepts it. Write `ip.addr !== 192.0.2.10` if any-not-equal is really what you want, but on two-address fields that matches nearly every packet, so the intent was usually `ip.addr != 192.0.2.10` or `!(ip.addr == 192.0.2.10)`.
  • Does `tcp.port not in {80, 443}` behave like `!=` or like `!(... in ...)`?
    Like `!=`. It hides any TCP packet in which either port is 80 or 443, the negation of `in`. Like every comparison it also needs the field, so UDP, ARP and other non-TCP frames are hidden as well. Write `not tcp.port in {80, 443}` to keep them visible.

A packet with two addresses is like a letter with a sender and a recipient. 'Not addressed to or from Ana' (!=) means neither name is Ana; 'some name on it is not Ana' (!==) is true of nearly every letter Ana sends.

saying these in an interview costs you the question

  • ip.addr != X still matches almost every packet, so wrap the test in !( ).
  • != and !(... == ...) behave identically on every frame.
  • === is a stricter alias for == that only adds a type check.
  • ~= is still the operator to use for any-not-equal in 4.6.
  • ip.addr holds one value per packet, the source address.