Nmap Development mailing list archives
Optimizing BPF filters
From: Daniel Miller <bonsaiviking () gmail com>
Date: Wed, 29 Oct 2014 00:14:36 -0500
I am about to commit 2 changes to the way we call pcap_compile in libnetutil: diff --git a/libnetutil/netutil.cc b/libnetutil/netutil.cc index b7727f7..5350660 100644 --- a/libnetutil/netutil.cc +++ b/libnetutil/netutil.cc @@ -4069,7 +4069,7 @@ void set_pcap_filter(const char *device, pcap_t *pd, const char *bpf, ...) { netutil_fatal("%s called with too-large filter arg\n", __func__); va_end(ap); - if (pcap_compile(pd, &fcode, buf, 0, 0) < 0) + if (pcap_compile(pd, &fcode, buf, 1, PCAP_NETMASK_UNKNOWN) < 0) netutil_fatal("Error compiling our pcap filter: %s", pcap_geterr(pd)); if (pcap_setfilter(pd, &fcode) < 0) netutil_fatal("Failed to set the pcap filter: %s\n", pcap_geterr(pd)); The first change is requesting optimization of our BPF via the third argument. The libpcap BPF optimizer is very good at what it does. It adds extra processing up front, but since we limit (in scan_engine_raw.cc:begin_sniffer) the number of individual addresses in a single filter to 20, it's not a noticeable slowdown. It does, however, reduce the number of BPF bytecode instructions for the big-scan case (where we don't filter on source) to 10 from 45. In the best case, the unoptimized code requires 3 loads and 3 jumps before returning (false), whereas the optimized requires only 1 load and 1 jump. Unoptimized: sudo tcpdump -dO -- 'dst host 1 and (icmp or icmp6 or tcp or udp or sctp)' (000) ldh [12] (001) jeq #0x800 jt 2 jf 4 (002) ld [30] (003) jeq #0x1 jt 12 jf 4 (004) ldh [12] (005) jeq #0x806 jt 6 jf 8 (006) ld [38] (007) jeq #0x1 jt 12 jf 8 (008) ldh [12] (009) jeq #0x8035 jt 10 jf 45 (010) ld [38] (011) jeq #0x1 jt 12 jf 45 (012) ldh [12] (013) jeq #0x800 jt 14 jf 16 (014) ldb [23] (015) jeq #0x1 jt 44 jf 16 <snip> (044) ret #65535 (045) ret #0 Optimized: sudo tcpdump -d -- 'dst host 1 and (icmp or icmp6 or tcp or udp or sctp)' (000) ldh [12] (001) jeq #0x800 jt 2 jf 10 (002) ld [30] (003) jeq #0x1 jt 4 jf 10 (004) ldb [23] (005) jeq #0x1 jt 9 jf 6 (006) jeq #0x6 jt 9 jf 7 (007) jeq #0x11 jt 9 jf 8 (008) jeq #0x84 jt 9 jf 10 (009) ret #65535 (010) ret #0 I'm guessing this won't matter in most cases, but it may be incrementally better in the super-fast Internet-scale scans that are getting lots of press these days, or in local-LAN scans on gigabit or faster networks. The second change is changing the netmask argument from 0 to PCAP_NETMASK_UNKNOWN. This should not affect Nmap at all for now, but may serve as a warning if anyone tries to introduce a BPF filter that uses the "broadcast" test. In order for this test to have meaning, we have to provide a real netmask for the address/interface we are listening on. We used to have code that did detection of this value, but it was stripped in r23778, so I suspect it was incompatible with IPv6 scans. Anyway, PCAP_NETMASK_UNKNOWN is the documented value to use in this case, so that's what I'm going with. Dan _______________________________________________ Sent through the dev mailing list http://nmap.org/mailman/listinfo/dev Archived at http://seclists.org/nmap-dev/
Current thread:
- Optimizing BPF filters Daniel Miller (Oct 28)