Skip to content

Commit 6ba0390

Browse files
authored
libpcap: show error when a BPF filter fails to compile (#5022)
Closes #4587. compile_filter() reported "Failed to compile filter expression X (-1)", giving no hint why the filter was rejected: a syntax error, or a filter incompatible with the link-layer type (for example a wlan filter on an Ethernet interface). Compile through a pcap handle in both the linktype and interface paths (pcap_open_dead needs neither an interface nor root) and, on failure, retrieve the message from pcap_geterr(). The exception now reads e.g. "... (802.11 link-layer types supported only on 802.11)" or "... (can't parse filter expression: syntax error)". Adds a regression test in test/regression.uts. AI-Assisted: yes (Claude Opus 4.8) Co-authored-by: Eugen Goebel <eugen-goebel@users.noreply.github.com>
1 parent abdf889 commit 6ba0390

2 files changed

Lines changed: 30 additions & 8 deletions

File tree

scapy/arch/common.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,9 @@ def compile_filter(filter_exp, # type: str
7979
from scapy.libs.winpcapy import (
8080
PCAP_ERRBUF_SIZE,
8181
pcap_open_live,
82+
pcap_open_dead,
8283
pcap_compile,
83-
pcap_compile_nopcap,
84+
pcap_geterr,
8485
pcap_close
8586
)
8687
except OSError:
@@ -109,9 +110,9 @@ def compile_filter(filter_exp, # type: str
109110
# Some conversion aliases (e.g. linktype_to_dlt in libpcap)
110111
if linktype == DLT_RAW_ALT:
111112
linktype = DLT_RAW
112-
ret = pcap_compile_nopcap(
113-
MTU, linktype, ctypes.byref(bpf), bpf_filter, 1, -1
114-
)
113+
# Use a "dead" capture handle (no interface / no root required) so that,
114+
# on failure, the libpcap error message can be retrieved with pcap_geterr
115+
pcap = pcap_open_dead(linktype, MTU)
115116
elif iface:
116117
err = create_string_buffer(PCAP_ERRBUF_SIZE)
117118
iface_b = create_string_buffer(network_name(iface).encode("utf8"))
@@ -121,14 +122,22 @@ def compile_filter(filter_exp, # type: str
121122
error = decode_locale_str(bytearray(err).strip(b"\x00"))
122123
if error:
123124
raise OSError(error)
124-
ret = pcap_compile(
125-
pcap, ctypes.byref(bpf), bpf_filter, 1, -1
125+
else:
126+
raise Scapy_Exception("Please provide an interface or linktype!")
127+
ret = pcap_compile(
128+
pcap, ctypes.byref(bpf), bpf_filter, 1, -1
129+
)
130+
if ret == -1:
131+
# Retrieve the underlying libpcap error message: it explains why the
132+
# filter is invalid or incompatible with the link-layer type
133+
errstr = decode_locale_str(
134+
bytearray(pcap_geterr(pcap)).strip(b"\x00")
126135
)
127136
pcap_close(pcap)
128-
if ret == -1:
129137
raise Scapy_Exception(
130-
"Failed to compile filter expression %s (%s)" % (filter_exp, ret)
138+
"Failed to compile filter expression %r (%s)" % (filter_exp, errstr)
131139
)
140+
pcap_close(pcap)
132141
return bpf
133142

134143

test/regression.uts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2478,6 +2478,19 @@ except Scapy_Exception:
24782478
else:
24792479
assert False
24802480

2481+
= compile_filter() surfaces the libpcap error on an invalid filter, GH#4587
2482+
~ libpcap
2483+
from scapy.arch.common import compile_filter
2484+
from scapy.data import DLT_EN10MB
2485+
try:
2486+
compile_filter("not arpand not port 22", linktype=DLT_EN10MB)
2487+
except Scapy_Exception as e:
2488+
# The message now includes the underlying libpcap reason instead of just a
2489+
# return code, e.g. a syntax error or a link-layer incompatibility:
2490+
assert "syntax error" in str(e).lower()
2491+
else:
2492+
assert False
2493+
24812494
= Check offline sniff with lfilter
24822495
assert len(sniff(offline=[IP()/UDP(), IP()/TCP()], lfilter=lambda x: TCP in x)) == 1
24832496

0 commit comments

Comments
 (0)