Libpcap BPF (BSD Packet Filter) packet filtering mechanism

Source: Internet
Author: User

Http://hi.baidu.com/ahtaria/blog/item/969ae4447eaa59076a63e57d.html

Libpcap focuses on the BPF (BSD Packet Filter) packet filtering mechanism. BPF was designed in 1992 to solve the inefficiency of the existing filtering mechanism. The BPF procedure is as follows: when a data packet reaches the network interface, the data link layer driver transmits it to the system protocol stack. However, if the BPF listening interface is used, the driver first calls BPF. BPF first filters data packets and stores them in the buffer zone related to the filter. Finally, the device driver obtains control again. It is noted that BPF first filters data packets and then caches them, avoiding
The nit filtering mechanism first caches each data packet until the user reads the data and then filters the efficiency issues. References D is the most important document about BPF design ideas.

The design concept of BPF is closely related to the development of computer hardware at that time. Compared with the old-fashioned filtering method CSPF (CMU/Stanford Packet Filter), BPF has two major features. 1: register-based filtering, instead of early memory stack filtering. 2: independent and non-shared memory buffers are directly used. At the same time, BPF has made great progress in the filtering algorithm. It uses a non-Ring Control Flow Graph (CFG Control Flow Graph) instead of an old-fashioned Boolean Expression Tree ). The Boolean Expression Tree is more intuitive to understand. Each of its leaf nodes is a predicate judgment, and not a leaf node is
And operation or operation. CSPF has three major drawbacks. 1: The stack used by the filter operation is simulated in the memory. To maintain the stack pointer, a number of addition/subtraction operations are required. The memory operation is the main bottleneck of the modern computer architecture. 2: The Boolean Expression Tree causes unnecessary repeated computation. 3: The Variable Length header of the data packet cannot be analyzed. The CFG algorithm used by BPF is actually a special state machine. Each node represents a predicate judgment, and the left and right sides correspond to the jump after the judgment fails and the success, after the jump, it is determined by a predicate. In this way, operations are performed repeatedly until the end point of success or failure is reached. The advantage of the CFG algorithm is that it directly establishes the analysis information of data packets in the graph, so that repeated computation is not required. Intuitively, cfg
Is a "fast, always forward" algorithm.

Filter code compilation

The implementation of BPF code on the CFG algorithm is very complex. It uses the pseudo-machine method. The BPF pseudo machine is a lightweight and efficient state machine that explains the BPF filtering code. The BPF filtering code is in the form of "opcode jt jf k", which represents the operation code and addressing method, correct jump judgment, failed jump judgment, and general data domains used for operations. The BPF filtering code is logically similar to the assembly language, but it is actually a machine language. It is noted that the data types of the above four domains are int and char. Obviously, writing and filtering code by users is too complicated. Therefore, libpcap
Allows you to write high-level, easy-to-understand filter strings and compile them into BPF code.

Libpcap uses four source programs gencode. c. Optimize. c. grammar. c. Example. c completes the compilation. The first two of them implement compilation and optimization of the filter string. The last two are mainly used to provide protocol-related filtering conditions for compilation and protocol-independent (character array) location Information ing, which is generated by the vocabulary analyzer generator flex and bison. See references C for instructions on these two tools.

Flex-ppcap _-T release. L> $. Release. C; MV $. Release. C Release. c

Bison-y-P pcap _-D grammar. Y

Mv y. Tab. c grammar. c

Mv y. Tab. h tokdefs. h

The pcap_compile () [getcode. C] function is called to compile the filter string. The format is:

Int pcap_compile (pcap_t * P, struct bpf_program * program,

Char * Buf, int optimize, bpf_u_int32 mask)

The Buf points to the user's filter string, and the compiled BPF code exists in the structure bpf_program, marking whether optimize indicates whether to optimize the BPF code.

/* [Pcap-bpf.h] */

Struct bpf_program {

U_int bf_len;/* Number of predicate judgment commands in BPF Code */

Struct bpf_insn * bf_insns;/* First predicate judgment command */

};

/* Predicate the command structure, which is included in the [pcap-bpf.h] previously described */

Struct bpf_insn {

U_short code;

U_char JT;

U_char JF;

Bpf_int32 K;

};

Filter code Installation

As mentioned above, filtering data packets in the kernel space is crucial to the efficiency of the entire capture mechanism. Early Linux using sock_packet does not support kernel filtering, so the filtering operation can only be performed in the user space (see pcap_read_packet () Code), in UNIX Network Programming (Volume 1) this is clearly described in Chapter 26th (reference B. But now it seems that the situation has changed. Linux supports kernel filtering on the pf_packet socket. Linux Kernel allows us
Packet Filter) is directly stored in the pf_packet socket processing process. The filter is executed immediately after the NIC receives the interrupted execution. LSF is based on the BPF mechanism, but the implementation of the two is slightly different. The actual code is as follows:

/* Append BPF code [pcap-linux.c] on the package capture device */

Static int

Pcap_setfilter_linux (pcap_t * handle, struct bpf_program * filter)

{

# Ifdef so_attach_filter

Struct sock_fprog fcode;

Int can_filter_in_kernel;

Int err = 0;

# Endif

/* Check whether the handle and filter structure are correct */

If (! Handle)

Return-1;

If (! Filter ){

Strncpy (handle-> errbuf, "setfilter: No filter specified ",

Sizeof (handle-> errbuf ));

Return-1;

}

/* The specific description is as follows */

If (install_bpf_program (handle, filter) <0)

Return-1;

/* The filter is run in the user space by default, but if the kernel is successfully installed, the value is 1 */

Handle-> Md. use_bpf = 0;

/* Try to install the filter in the kernel */

# Ifdef so_attach_filter

# Ifdef ushrt_max

If (handle-> fcode. bf_len> ushrt_max ){

/* The filter code is too long and the kernel does not support it */

Fprintf (stderr, "Warning: Filter too complex for Kernel \ n ");

Fcode. Filter = NULL;

Can_filter_in_kernel = 0;

} Else

# Endif/* ushrt_max */

{

/* The data structure used to set the filter in Linux kernel is sock_fprog, instead of BPF structure bpf_program. Therefore, conversions between structures should be performed */

Switch (fix_program (handle, & fcode )){

/* Serious error. Exit directly */

Case-1:

Default:

Return-1;

/* Check passed, but cannot work in the kernel */

Case 0:

Can_filter_in_kernel = 0;

Break;

/* BPF can work in the kernel */

Case 1:

Can_filter_in_kernel = 1;

Break;

}

}

/* If you can filter data in the kernel, install the filter to the kernel */

If (can_filter_in_kernel ){

If (ERR = set_kernel_filter (handle, & fcode) = 0)

{

/* Installation successful !!! */

Handle-> Md. use_bpf = 1;

}

Else if (ERR =-1)/* Non-Fatal Error */

{

If (errno! = Enoprotoopt & errno! = Eopnotsupp ){

Fprintf (stderr, "Warning: Kernel filter failed:

% S \ n ", pcap_strerror (errno ));

}

}

}

/* If the filter cannot be used in the kernel, remove the socket

The kernel filter installed on. The main purpose is to avoid the interference of the existing filter on packet filtering */

If (! Handle-> Md. use_bpf)

Reset_kernel_filter (handle); [pcap-linux.c]

# Endif

}

/* Copy the BPF code to the fcode of the pcap_t Data Structure */

Int install_bpf_program (pcap_t * P, struct bpf_program * FP)

{

Size_t prog_size;

/* Release the BPF code that may already exist */

Pcap_freecode (& P-> fcode );

/* Calculate the length of the filtering code and allocate memory space */

Prog_size = sizeof (* FP-> bf_insns) * FP-> bf_len;

P-> fcode. bf_len = FP-> bf_len;

P-> fcode. bf_insns = (struct bpf_insn *) malloc (prog_size );

If (p-> fcode. bf_insns = NULL ){

Snprintf (p-> errbuf, sizeof (p-> errbuf ),

"Malloc: % s", pcap_strerror (errno ));

Return (-1 );

}

/* Save the filter code in the capture handle */

Memcpy (p-> fcode. bf_insns, FP-> bf_insns, prog_size );

Return (0 );

}

/* Install the filter in the kernel */

Static int set_kernel_filter (pcap_t * handle, struct sock_fprog * fcode)

{

Int total_filter_on = 0;

Int save_mode;

Int ret;

Int save_errno;

/* Before setting the filter, several data packets may already exist in the socket data packet receiving queue. After the filter is set,

These packets may not meet the filtering conditions, but they are not discarded by the filter.

This means that the first packet transmitted to the user space does not meet the filtering conditions.

Note that filtering in user space is not a problem because the user space filter is executed after the package enters the queue.

Libpcap solves this problem by setting the filter,

First, read all the data packets in the receiving queue. The procedure is as follows. */

/* To avoid infinite loops (repeatedly reading and dropping data packets, but new data packets arrive continuously), first set a filter to prevent all packets from entering */

Setsockopt (handle-> FD, sol_socket, so_attach_filter,

& Total_fcode, sizeof (total_fcode );

/* Save the current socket property */

Save_mode = fcntl (handle-> FD, f_getfl, 0 );

/* Set the socket to non-blocking mode */

Fcntl (handle-> FD, f_setfl, save_mode | o_nonblock );

/* Read the data packets in the queue repeatedly until no data packets are readable. This means that the receiving queue has been cleared */

While (Recv (handle-> FD, & drain, sizeof drain, msg_trunc)> = 0 );

/* Restore saved socket attributes */

Fcntl (handle-> FD, f_setfl, save_mode );

/* Install a new filter now */

Setsockopt (handle-> FD, sol_socket, so_attach_filter,

Fcode, sizeof (* fcode ));

}

/* Release the possible kernel filters on the socket */

Static int reset_kernel_filter (pcap_t * handle)

{

Int dummy;

Return setsockopt (handle-> FD, sol_socket, so_detach_filter,

& Dummy, sizeof (dummy ));

}

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.