]> The Tcpdump Group git mirrors - libpcap/commitdiff
Require a live capture for all Linux BPF extensions.
authorDenis Ovsienko <[email protected]>
Thu, 23 Jan 2025 19:13:38 +0000 (19:13 +0000)
committerDenis Ovsienko <[email protected]>
Sat, 25 Jan 2025 11:56:42 +0000 (11:56 +0000)
On Linux compiling a filter that includes "inbound", "outbound" or
"ifindex" for a pcap_t that was created using pcap_open_dead() produces
bytecode that uses BPF extensions if the DLT is not one of the known
DLTs:

$ filtertest RAW '(inbound and ifindex 1) or (outbound and ifindex 2)'
(000) ldh      [type]
(001) jeq      #0x4             jt 2 jf 4
(002) ld       [ifidx]
(003) jeq      #0x2             jt 6 jf 7
(004) ld       [ifidx]
(005) jeq      #0x1             jt 6 jf 7
(006) ret      #262144
(007) ret      #0

This is because both gen_ifindex() and gen_inbound_outbound() assume
that a live capture is equivalent to rfile == NULL, which is not true.

Fix this by using the logic that already works correctly for the "vlan"
keyword.  Define a new flag for bpf_codegen_flags and set it in
setup_socket() (which is in the code path for a live capture only) if
any BPF extensions are available.  Check for the flag in a new helper
function and use it instead of the incorrect rfile tests.  Set the flag
in filtertest as well.  Now pcap_compile() handles both cases correctly
on Linux:

$ filtertest RAW 'ifindex 1'
filtertest: ifindex not supported on Raw IP (not a live capture)

$ filtertest -l RAW 'ifindex 1'
(000) ld       [ifidx]
(001) jeq      #0x1             jt 2 jf 3
(002) ret      #262144
(003) ret      #0

Make two existing accept tests for "inbound" and "outbound" specific to
the extensions and add a similar test for "ifindex".  Add more reject
tests to cover the three keywords on Linux w/o extensions and non-Linux
OSes separately.

CHANGES
gencode.c
pcap-int.h
pcap-linux.c
testprogs/TESTrun
testprogs/filtertest.c

diff --git a/CHANGES b/CHANGES
index 32fa61f0e819f3e382ce4ff8230298480fa1366f..85f8fccc4dd0d87d6b4cceb16450311dfde743dc 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -48,6 +48,7 @@ DayOfTheWeek, Month DD, YYYY / The Tcpdump Group
       Require "[wlan] dir" integer value to be within range.
       Fix the != comparison for ATM and MTP field values.
       Deprecate bpf_filter().
+      Require a live capture for all Linux BPF extensions.
     rpcap:
       Support user names and passwords in rpcap:// and rpcaps:// URLs.
       Add a -t flag to rpcapd to specify the data channel port; from
index 6b0014ac38226346d805c80b33d14d45710a8ece..1dbff96dca5e68a1e087b645470cee88b28eb4eb 100644 (file)
--- a/gencode.c
+++ b/gencode.c
@@ -8662,6 +8662,25 @@ gen_multicast(compiler_state_t *cstate, int proto)
        /*NOTREACHED*/
 }
 
+#ifdef __linux__
+/*
+ * This is Linux; we require PF_PACKET support.  If this is a *live* capture,
+ * we can look at special meta-data in the filter expression; otherwise we
+ * can't because it is either a savefile (rfile != NULL) or a pcap_t created
+ * using pcap_open_dead() (rfile == NULL).  Thus check for a flag that
+ * pcap_activate() conditionally sets.
+ */
+static void
+require_basic_bpf_extensions(compiler_state_t *cstate, const char *keyword)
+{
+       if (cstate->bpf_pcap->bpf_codegen_flags & BPF_SPECIAL_BASIC_HANDLING)
+               return;
+       bpf_error(cstate, "%s not supported on %s (not a live capture)",
+           keyword,
+           pcap_datalink_val_to_description_or_dlt(cstate->linktype));
+}
+#endif // __linux__
+
 struct block *
 gen_ifindex(compiler_state_t *cstate, int ifindex)
 {
@@ -8684,18 +8703,7 @@ gen_ifindex(compiler_state_t *cstate, int ifindex)
                break;
        default:
 #if defined(__linux__)
-               /*
-                * This is Linux; we require PF_PACKET support.
-                * If this is a *live* capture, we can look at
-                * special meta-data in the filter expression;
-                * if it's a savefile, we can't.
-                */
-               if (cstate->bpf_pcap->rfile != NULL) {
-                       /* We have a FILE *, so this is a savefile */
-                       bpf_error(cstate, "ifindex not supported on %s when reading savefiles",
-                           pcap_datalink_val_to_description_or_dlt(cstate->linktype));
-                       /*NOTREACHED*/
-               }
+               require_basic_bpf_extensions(cstate, "ifindex");
                /* match ifindex */
                b0 = gen_cmp(cstate, OR_LINKHDR, SKF_AD_OFF + SKF_AD_IFINDEX, BPF_W,
                             ifindex);
@@ -8816,18 +8824,7 @@ gen_inbound_outbound(compiler_state_t *cstate, const int outbound)
                 * in pcapng files.
                 */
 #if defined(__linux__)
-               /*
-                * This is Linux; we require PF_PACKET support.
-                * If this is a *live* capture, we can look at
-                * special meta-data in the filter expression;
-                * if it's a savefile, we can't.
-                */
-               if (cstate->bpf_pcap->rfile != NULL) {
-                       /* We have a FILE *, so this is a savefile */
-                       bpf_error(cstate, "inbound/outbound not supported on %s when reading savefiles",
-                           pcap_datalink_val_to_description_or_dlt(cstate->linktype));
-                       /*NOTREACHED*/
-               }
+               require_basic_bpf_extensions(cstate, outbound ? "outbound" : "inbound");
                /* match outgoing packets */
                b0 = gen_cmp(cstate, OR_LINKHDR, SKF_AD_OFF + SKF_AD_PKTTYPE, BPF_H,
                             PACKET_OUTGOING);
index 5f97aaab520aa4c60e407fa9ae88b1603ca22b28..195267b544e7e72052d0124bcbba63089f4918ea 100644 (file)
@@ -368,6 +368,12 @@ struct pcap {
  * BPF code generation flags.
  */
 #define BPF_SPECIAL_VLAN_HANDLING      0x00000001      /* special VLAN handling for Linux */
+/*
+ * Special handling of packet type and ifindex, which are some of the auxiliary
+ * data items available in Linux >= 2.6.27.  Disregard protocol and netlink
+ * attributes for now.
+ */
+#define BPF_SPECIAL_BASIC_HANDLING     0x00000002
 
 /*
  * User data structure for the one-shot callback used for pcap_next()
index ac7e668aad0235977d44d009aa39ebcd32635a2c..8fca8b60b484671b68b65d2032886ad345b21450 100644 (file)
@@ -2762,7 +2762,7 @@ setup_socket(pcap_t *handle, int is_any_device)
         */
        handle->fd = sock_fd;
 
-#if defined(SO_BPF_EXTENSIONS) && defined(SKF_AD_VLAN_TAG_PRESENT)
+#ifdef SO_BPF_EXTENSIONS
        /*
         * Can we generate special code for VLAN checks?
         * (XXX - what if we need the special code but it's not supported
@@ -2770,14 +2770,23 @@ setup_socket(pcap_t *handle, int is_any_device)
         */
        if (getsockopt(sock_fd, SOL_SOCKET, SO_BPF_EXTENSIONS,
            &bpf_extensions, &len) == 0) {
+               /*
+                * This is a live capture with some BPF extensions support,
+                * so indicate that at least the auxiliary data items from
+                * Linux 2.6.27 are available (this concerns SKF_AD_PKTTYPE
+                * and SKF_AD_IFINDEX in the first place).
+                */
+               handle->bpf_codegen_flags |= BPF_SPECIAL_BASIC_HANDLING;
+#ifdef SKF_AD_VLAN_TAG_PRESENT
                if (bpf_extensions >= SKF_AD_VLAN_TAG_PRESENT) {
                        /*
                         * Yes, we can.  Request that we do so.
                         */
                        handle->bpf_codegen_flags |= BPF_SPECIAL_VLAN_HANDLING;
                }
+#endif // SKF_AD_VLAN_TAG_PRESENT
        }
-#endif /* defined(SO_BPF_EXTENSIONS) && defined(SKF_AD_VLAN_TAG_PRESENT) */
+#endif // SO_BPF_EXTENSIONS
 
        return status;
 }
index ec9f36e1557a0bf975756193940b4512642837cf..42a65a4405b29063ee00736f7d0a0516ae6278c1 100755 (executable)
@@ -490,12 +490,9 @@ my %accept_blocks = (
                        (004) ret      #0
                        EOF
        }, # juniper_mfr_outbound
-       # The two tests below represent the current Linux-specific implementation,
-       # which is not consistent in which function -- pcap_compile() or
-       # pcap_setfilter() -- and under which conditions patches the bytecode for
-       # specific keywords.  So this behaviour may change in future.
-       linux_skf_ad_inbound => {
+       inbound_linuxext => {
                skip => is_not_linux(),
+               linuxext => 1,
                DLT => 'EN10MB',
                expr => 'inbound',
                unopt => <<~'EOF',
@@ -504,9 +501,10 @@ my %accept_blocks = (
                        (002) ret      #0
                        (003) ret      #262144
                        EOF
-       }, # linux_skf_ad_inbound
-       linux_skf_ad_outbound => {
+       }, # inbound_linuxext
+       outbound_linuxext => {
                skip => is_not_linux(),
+               linuxext => 1,
                DLT => 'EN10MB',
                expr => 'outbound',
                unopt => <<~'EOF',
@@ -515,7 +513,19 @@ my %accept_blocks = (
                        (002) ret      #262144
                        (003) ret      #0
                        EOF
-       }, # linux_skf_ad_inbound
+       }, # outbound_linuxext
+       ifindex_linuxext => {
+               skip => is_not_linux(),
+               linuxext => 1,
+               DLT => 'EN10MB',
+               expr => 'ifindex 10',
+               unopt => <<~'EOF',
+                       (000) ld       [ifidx]
+                       (001) jeq      #0xa             jt 2    jf 3
+                       (002) ret      #262144
+                       (003) ret      #0
+                       EOF
+       }, # ifindex_linuxext
 
        mtp2_fisu => {
                DLT => 'MTP2',
@@ -5776,17 +5786,41 @@ my %reject_tests = (
                expr => 'mpls 1048576',
                errstr => 'greater than maximum',
        },
-       inbound_not_supported => {
+       inbound_not_supported_linux => {
+               skip => is_not_linux(),
+               DLT => 'EN10MB',
+               expr => 'inbound',
+               errstr => 'not a live capture',
+       },
+       outbound_not_supported_linux => {
+               skip => is_not_linux(),
+               DLT => 'EN10MB',
+               expr => 'outbound',
+               errstr => 'not a live capture',
+       },
+       ifindex_not_supported_linux => {
+               skip => is_not_linux(),
+               DLT => 'LINUX_SLL',
+               expr => 'ifindex 1',
+               errstr => 'not a live capture',
+       },
+       inbound_not_supported_other => {
                skip => is_linux(),
                DLT => 'EN10MB',
                expr => 'inbound',
-               errstr => 'inbound/outbound not supported',
+               errstr => 'not supported',
        },
-       outbound_not_supported => {
+       outbound_not_supported_other => {
                skip => is_linux(),
                DLT => 'EN10MB',
                expr => 'outbound',
-               errstr => 'inbound/outbound not supported',
+               errstr => 'not supported',
+       },
+       ifindex_not_supported_other => {
+               skip => is_linux(),
+               DLT => 'EN10MB',
+               expr => 'ifindex 1',
+               errstr => 'not supported',
        },
 );
 
index fd177b7d8da4c95c3d634cd761af73079f62e37b..e669c1f62472bcf0be25c12bd36a34266d1bf531 100644 (file)
@@ -362,6 +362,7 @@ main(int argc, char **argv)
 #ifdef LINUX_BPF_EXT
        if (lflag) {
                pd->bpf_codegen_flags |= BPF_SPECIAL_VLAN_HANDLING;
+               pd->bpf_codegen_flags |= BPF_SPECIAL_BASIC_HANDLING;
        }
 #endif