]> The Tcpdump Group git mirrors - libpcap/commitdiff
Use BPF extensions in compiled filters 391/head
authorMichal Sekletar <[email protected]>
Fri, 31 Oct 2014 14:19:54 +0000 (15:19 +0100)
committerMichal Sekletar <[email protected]>
Mon, 3 Nov 2014 13:54:18 +0000 (14:54 +0100)
libpcap will generate BPF filter code which uses BPF extensions if target
platform supports them. Currently supported BPF extensions are vlan_tci and
vlan_pr.

Also to properly handle such filters when filtering in userspace libpcap now
employs bpf_filter1.

gencode.c
pcap-linux.c

index 4a97aba46d2c877e63e446eb43bd1d6fffdc5ea8..5967cb1355efe0d148cc7484d299b4431747c4e4 100644 (file)
--- a/gencode.c
+++ b/gencode.c
@@ -54,6 +54,7 @@
 
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#include <errno.h>
 
 #endif /* WIN32 */
 
@@ -131,9 +132,9 @@ static pcap_t *bpf_pcap;
 
 /* Hack for updating VLAN, MPLS, and PPPoE offsets. */
 #ifdef WIN32
-static u_int   orig_linktype = (u_int)-1, orig_nl = (u_int)-1, label_stack_depth = (u_int)-1;
+static u_int   orig_linktype = (u_int)-1, orig_nl = (u_int)-1, label_stack_depth = (u_int)-1, vlan_stack_depth = (u_int)-1;
 #else
-static u_int   orig_linktype = -1U, orig_nl = -1U, label_stack_depth = -1U;
+static u_int   orig_linktype = -1U, orig_nl = -1U, label_stack_depth = -1U, vlan_stack_depth = -1U;
 #endif
 
 /* XXX */
@@ -963,6 +964,7 @@ init_linktype(p)
        orig_linktype = -1;
        orig_nl = -1;
         label_stack_depth = 0;
+        vlan_stack_depth = 0;
 
        reg_off_ll = -1;
        reg_off_macpl = -1;
@@ -8045,6 +8047,76 @@ gen_ahostop(eaddr, dir)
        /* NOTREACHED */
 }
 
+#if defined(SKF_AD_VLAN_TAG) && defined(SKF_AD_VLAN_TAG_PRESENT)
+static int skf_ad_vlan_tag_present_supported(int bpf_extensions) {
+        return bpf_extensions >= SKF_AD_VLAN_TAG_PRESENT;
+}
+
+static struct block *
+gen_vlan_bpf_extensions(int vlan_num) {
+        struct block *b0, *b1;
+        struct slist *s;
+        int val = 0, len, r;
+
+        len = sizeof(val);
+        r = getsockopt(bpf_pcap->fd, SOL_SOCKET, SO_BPF_EXTENSIONS, &val, &len);
+        if (r < 0)
+                return NULL;
+
+        if (!skf_ad_vlan_tag_present_supported(val))
+                return NULL;
+
+        /* generate new filter code based on extracting packet
+         * metadata */
+        s = new_stmt(BPF_LD|BPF_B|BPF_ABS);
+        s->s.k = SKF_AD_OFF + SKF_AD_VLAN_TAG_PRESENT;
+
+        b0 = new_block(JMP(BPF_JEQ));
+        b0->stmts = s;
+        b0->s.k = 1;
+
+        if (vlan_num >= 0) {
+                s = new_stmt(BPF_LD|BPF_B|BPF_ABS);
+                s->s.k = SKF_AD_OFF + SKF_AD_VLAN_TAG;
+
+                b1 = new_block(JMP(BPF_JEQ));
+                b1->stmts = s;
+                b1->s.k = (bpf_int32) vlan_num;
+
+                gen_and(b0,b1);
+                b0 = b1;
+        }
+
+        return b0;
+}
+#endif
+
+static struct block *
+gen_vlan_no_bpf_extensions(int vlan_num) {
+        struct block *b0, *b1;
+
+        /* check for VLAN, including QinQ */
+        b0 = gen_cmp(OR_LINK, off_linktype, BPF_H,
+                     (bpf_int32)ETHERTYPE_8021Q);
+        b1 = gen_cmp(OR_LINK, off_linktype, BPF_H,
+                     (bpf_int32)ETHERTYPE_8021QINQ);
+        gen_or(b0,b1);
+        b0 = b1;
+
+        /* If a specific VLAN is requested, check VLAN id */
+        if (vlan_num >= 0) {
+                b1 = gen_mcmp(OR_MACPL, 0, BPF_H,
+                              (bpf_int32)vlan_num, 0x0fff);
+                gen_and(b0, b1);
+                b0 = b1;
+        }
+
+        off_macpl += 4;
+        off_linktype += 4;
+
+        return b0;
+}
+
 /*
  * support IEEE 802.1Q VLAN trunk over ethernet
  */
@@ -8096,36 +8168,24 @@ gen_vlan(vlan_num)
        case DLT_EN10MB:
        case DLT_NETANALYZER:
        case DLT_NETANALYZER_TRANSPARENT:
-               /* check for VLAN, including QinQ */
-               b0 = gen_cmp(OR_LINK, off_linktype, BPF_H,
-                   (bpf_int32)ETHERTYPE_8021Q);
-               b1 = gen_cmp(OR_LINK, off_linktype, BPF_H,
-                   (bpf_int32)ETHERTYPE_8021QINQ);
-               gen_or(b0,b1);
-               b0 = b1;
-
-               /* If a specific VLAN is requested, check VLAN id */
-               if (vlan_num >= 0) {
-                       b1 = gen_mcmp(OR_MACPL, 0, BPF_H,
-                           (bpf_int32)vlan_num, 0x0fff);
-                       gen_and(b0, b1);
-                       b0 = b1;
-               }
-
-               off_macpl += 4;
-               off_linktype += 4;
-#if 0
-               off_nl_nosnap += 4;
-               off_nl += 4;
+#if defined(SKF_AD_VLAN_TAG) && defined(SKF_AD_VLAN_TAG_PRESENT)
+                if (!vlan_stack_depth) {
+                        b0 = gen_vlan_bpf_extensions(vlan_num);
+                        if (!b0)
+                                b0 = gen_vlan_no_bpf_extensions(vlan_num);
+                }
+                else
 #endif
-               break;
-
+                        b0 = gen_vlan_no_bpf_extensions(vlan_num);
+                break;
        default:
                bpf_error("no VLAN support for data link type %d",
                      linktype);
                /*NOTREACHED*/
        }
 
+        vlan_stack_depth++;
+
        return (b0);
 }
 
index a48ed40ba0da88cdfebae45518f736d4b7d59a4e..101ad942407358322baa4fa47c4a53ff57b3f823 100644 (file)
@@ -1484,6 +1484,7 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata)
        int                     packet_len, caplen;
        struct pcap_pkthdr      pcap_header;
 
+        struct bpf_aux_data     aux_data;
 #ifdef HAVE_PF_PACKET_SOCKETS
        /*
         * If this is a cooked device, leave extra room for a
@@ -1667,6 +1668,11 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata)
                        tag->vlan_tpid = htons(ETH_P_8021Q);
                        tag->vlan_tci = htons(aux->tp_vlan_tci);
 
+                        /* store vlan tci to bpf_aux_data struct for userland bpf filter */
+#if defined(TP_STATUS_VLAN_VALID)
+                        aux_data.vlan_tag = htons(aux->tp_vlan_tci) & 0x0fff;
+                        aux_data.vlan_tag_present = (aux->tp_status & TP_STATUS_VLAN_VALID);
+#endif
                        packet_len += VLAN_TAG_LEN;
                }
        }
@@ -1711,8 +1717,8 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata)
 
        /* Run the packet filter if not using kernel filter */
        if (handlep->filter_in_userland && handle->fcode.bf_insns) {
-               if (bpf_filter(handle->fcode.bf_insns, bp,
-                               packet_len, caplen) == 0)
+               if (bpf_filter1(handle->fcode.bf_insns, bp,
+                               packet_len, caplen, &aux_data) == 0)
                {
                        /* rejected by filter */
                        return 0;
@@ -4225,10 +4231,15 @@ static int pcap_handle_packet_mmap(
         * the filter when the ring became empty, but it can possibly
         * happen a lot later... */
        bp = frame + tp_mac;
-       if (handlep->filter_in_userland && handle->fcode.bf_insns &&
-                       (bpf_filter(handle->fcode.bf_insns, bp,
-                               tp_len, tp_snaplen) == 0))
-               return 0;
+       if (handlep->filter_in_userland && handle->fcode.bf_insns) {
+                struct bpf_aux_data aux_data;
+
+                aux_data.vlan_tag = tp_vlan_tci & 0x0fff;
+                aux_data.vlan_tag_present = tp_vlan_tci_valid;
+
+                if (bpf_filter1(handle->fcode.bf_insns, bp, tp_len, tp_snaplen, &aux_data) == 0)
+                        return 0;
+        }
 
        sll = (void *)frame + TPACKET_ALIGN(handlep->tp_hdrlen);
        if (!linux_check_direction(handle, sll))