From e80ed31b6e3ab778a7e0dd53348f488a91456cfc Mon Sep 17 00:00:00 2001 From: Michal Sekletar 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 #include +#include #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