Introduction
Berkeley Packet Filter (BPF) expressions describe the packets that a filter should match. X4B uses the familiar libpcap expression language for BPF match conditions in custom Layer 4 rules.
A BPF expression only selects packets. The Layer 4 rule's target determines what X4B does with a matching packet.
Start with a single condition
A condition, also called a primitive, tests one packet property. Begin with the narrowest condition that describes the traffic you want:
| Expression | Matches |
|---|---|
tcp | TCP packets |
udp | UDP packets |
tcp dst port 443 | TCP packets sent to destination port 443 |
udp port 53 | UDP packets whose source or destination port is 53 |
tcp src portrange 0-1023 | TCP packets from source ports 0 through 1023 |
src host 192.0.2.10 | Packets from one source address |
src net 192.0.2.0/24 | Packets from a source network |
greater 1000 | Packets at least 1,000 bytes long |
less 1000 | Packets no more than 1,000 bytes long |
Direction matters. src port 53 checks only the source port, dst port 53 checks only the destination port, and port 53 accepts either direction. Add tcp or udp when the transport protocol matters.
Combine conditions with logical operators
Use and, or, and not to build a rule from smaller conditions.
AND: require every condition
and matches only when both sides are true:
udp and dst port 53
This matches UDP packets sent to port 53. A TCP packet sent to port 53 does not match, and neither does a UDP packet sent to another port.
Add further conditions when all of them must apply:
tcp and dst port 443 and greater 1000
OR: allow alternatives
or matches when either side is true:
tcp dst port 80 or tcp dst port 443
This matches TCP traffic sent to either web port. Repeat qualifiers such as tcp dst port in each branch. Although libpcap can infer omitted qualifiers in some expressions, repeating them makes the intended match easier to review.
NOT: exclude a condition
not reverses the condition that follows it:
tcp and not dst port 22
This matches TCP packets except those sent to destination port 22.
Use parentheses when negating more than one alternative:
udp and not (src port 53 or dst port 53)
This matches UDP traffic only when neither endpoint uses port 53.
Use parentheses to make the logic explicit
Parentheses group conditions and make the order of evaluation unambiguous:
(tcp dst port 80 or tcp dst port 443) and greater 1000
The packet must be larger than 1,000 bytes and must be TCP traffic sent to port 80 or 443.
In libpcap syntax, not has the highest precedence. and and or have equal precedence and are evaluated from left to right. This differs from many programming languages, where and commonly binds more tightly than or.
For example:
tcp or udp and dst port 53
is evaluated as:
(tcp or udp) and dst port 53
It is not evaluated as tcp or (udp and dst port 53). Always add parentheses when an expression mixes and and or, even if the expression already appears to work.
Match flags, lengths, and packet bytes
Use TCP flag constants to select handshake packets:
| Expression | Matches |
|---|---|
tcp[tcpflags] == tcp-syn | TCP packets with only SYN set |
tcp[tcpflags] == (tcp-syn|tcp-ack) | TCP packets with SYN and ACK set |
Packet accessors use protocol[offset:length]. The length can be 1, 2, or 4 bytes:
udp[8:4] = 0x53414d50
For UDP, offset 8 begins immediately after the UDP header, so this expression compares the first four payload bytes. A packet that is too short for a requested byte range does not match.
Use bitmasks when only some bits matter. This expression matches TCP packets with SYN set, even if other flags are also present:
tcp[tcpflags] & tcp-syn != 0
Keep byte checks readable by grouping alternatives:
udp[8:4] = 0x53414d50 and (udp[18:1] = 0x70 or udp[18:1] = 0x69 or udp[18:1] = 0x63)
Build and review a rule
- Write one condition and confirm that it describes the intended protocol or port.
- Add one logical branch at a time.
- Put parentheses around every group that mixes
andandor. - Use
srcanddstexplicitly unless either direction is intentional. - Save only after the editor reports that the expression is valid.
- Keep the expression as small and specific as practical. Very large expressions may be rejected.
The editor checks syntax and compilation, but a valid expression can still select the wrong traffic. Read each branch aloud before applying a DROP rule, and begin with a narrowly scoped rule where possible.
Common patterns
| Goal | Expression |
|---|---|
| DNS traffic in either direction | udp port 53 |
| Inbound HTTPS traffic | tcp dst port 443 |
| Inbound HTTP or HTTPS traffic | tcp and (dst port 80 or dst port 443) |
| Small UDP packets | udp and less 100 |
| TCP SYN with no other flags | tcp[tcpflags] == tcp-syn |
| Any packet with TCP SYN set | tcp[tcpflags] & tcp-syn != 0 |
| Source network except SSH | src net 192.0.2.0/24 and not tcp dst port 22 |
| IP header contains options | ip[0] & 0x0f != 5 |
MicroBPF
MicroBPF is the limited form of BPF used by X4B to select packets for capture and other small tasks. You can write familiar libpcap expressions such as udp port 1234 (source or destination port), dst port 1234, or udp portrange 1000-2000.
Combine conditions with and, or, and not, or check bytes at a known position, for example udp[8:4] = 0x6d6a6974. Use payload[10:4] = 0x12345678 for a raw check without choosing a protocol. payload[...] also works in arithmetic, bitmasks, and comparisons with other fields. Ordinary filter expressions capture matching packets and reject the rest. Very large expressions may be rejected. Packet reads need a known maximum position within the configured capture length. A packet must be long enough for the deepest check in the expression, even when another condition could match earlier.
udp and tcp check the protocol in the IP header, so udp port 1234 will not match a TCP packet with the same port. Port checks and checks of transport or application data are supported; IP-address and MAC-address filters are unavailable. Data offsets start after the IP header, including any IP options: udp[8:4] checks four bytes after the UDP header. Non-initial IP fragments do not match. Expressions cannot change packets or remember previous packets. Advanced return expressions can return constants or selected packet bytes, but cannot return a calculated value. To access bytes at a set location in the packet, use payload[offset:length].
MicroBPF is used in Troubleshooting Packet Captures and in certain cases in Layer 4 rules transparently. It is not used in the Layer 4 rule editor, which uses full BPF expressions.
In Troubleshooting, select PCAP Capture and optionally enter a Capture expression (MicroBPF). Leave it blank to capture all packets matching the selected traffic type. The expression further restricts that traffic; it does not replace the filtering location or traffic selector. Capture filters can inspect up to 1,500 transport-relative bytes and compile to at most 64 instruction records, including return metadata.
