The third buffer in Figure 1 is the filter's memory array . It is used by both the filters in the kernel and the userspace application. User applications have read and write access to the memory arrays of their filters, so the arrays can be used to exchange data between the application and a filter expression. The area is persistent, i.e., its contents remain valid across multiple invocations of a filter. It is argued in [19] that the absence of persistent state is one of the major drawbacks of BPF. While [19] describes how BPF can be extended to also allow for persistent memory (and explicit switching between persistent and non-persistent memory is needed), this paper describes an approach in which it is part of the design from the outset.
A simple use case is a filter which treats the entire memory array
as a hash table that is used to count the number of packets received
on all TCP/IP flows. The corresponding filter first checks whether a
packet is TCP/IP. If so, it calculates a hash of the
<ipsrc,ipdest,srcport,dstport>
tuple and increments the counter
stored at that location in the memory array. The result is that
without intervention by the user application, the memory array
contains the packet counts of all TCP/IP flows seen by the system
(assuming the hash table is large enough). The implementation of this
example is trivial if the language is capable of using persistent
state. An example of such code in FPL-2 is shown in
Figure 6 and will be discussed in Section 3.3.1.