Blob Blame History Raw
From e80ed31b6e3ab778a7e0dd53348f488a91456cfc Mon Sep 17 00:00:00 2001
From: Michal Sekletar <msekleta@redhat.com>
Date: Fri, 31 Oct 2014 15:19:54 +0100
Subject: [PATCH 2/2] Use BPF extensions in compiled filters

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.

(cherry picked from 04660eb1e56102e2369473cae2538e4d3d263607)

Conflicts:
        pcap-linux.c
---
 gencode.c    | 110 +++++++++++++++++++++++++++++++++++++++++++++--------------
 pcap-linux.c |  23 +++++++++----
 2 files changed, 102 insertions(+), 31 deletions(-)

diff --git a/gencode.c b/gencode.c
index d58ea30..2ff725e 100644
--- a/gencode.c
+++ b/gencode.c
@@ -58,6 +58,7 @@ static const char rcsid[] _U_ =
 
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#include <errno.h>
 
 #endif /* WIN32 */
 
@@ -135,9 +136,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 */
@@ -962,6 +963,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;
@@ -7861,6 +7863,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
  */
@@ -7912,36 +7984,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);
 }
 
diff --git a/pcap-linux.c b/pcap-linux.c
index a0e543c..68e6d05 100644
--- a/pcap-linux.c
+++ b/pcap-linux.c
@@ -1475,6 +1475,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
@@ -1658,6 +1659,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;
 		}
 	}
@@ -1702,8 +1708,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;
@@ -4270,10 +4276,15 @@ static int pcap_handle_packet_mmap(
 		snaplen += sizeof(struct sll_header);
 	}
 
-        if (handlep->filter_in_userland && handle->fcode.bf_insns &&
-            (bpf_filter(handle->fcode.bf_insns, bp,
-                        tp_len, 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;
+        }
 
 	if (!linux_check_direction(handle, sll))
 		return 0;
-- 
2.4.3