In this article, we’ll share our experience on how to filter packets by location on an eBPF (extended Berkeley Packet Filter) space while maintaining high performance at a low computational cost. We’ll also share benchmarking results that confirm the efficiency of the solution.
In our infrastructure, we use the XDP framework to secure our customers against volumetric DDOS attacks. This architecture allows fast packet processing in the Linux kernel because XDP bypasses most of the network stack in the kernel. The firewall is highly configurable, and our customers can choose to filter packets from specific countries while maintaining high performance.
In this article, we’ll describe how we keep the firewall’s performance high when we need to geo-filter traffic or to:
You can use our solution if you face similar use cases. Those would be, for example, if your clients need to allow requests from one specific country (allow listing), block a specific location where malicious traffic comes from during a DDoS attack, or limit requests from other geolocations to DNS servers in a certain point of presence.
We decided to use GeoIP databases for the geo-filtering solution. Such databases consist of IP address prefixes and country codes in the ISO 3166-1 format to which these addresses belong. For example:
18.104.22.168/24 → NZ 22.214.171.124/32 → BE
GeoIP databases are very common. Many companies on the Internet allow you to buy such databases for a small cost and, sometimes, even to use them for free.
To implement the GeoIP databases for eBPF, we used maps, a key-value type of storage that enables data exchange between the user space and the kernel space.
The only difficulty that has to be solved is finding a way to maintain high performance at a low computational cost. To do this, we chose a wide bitmap so that each bit corresponds to one country.
We need to find the most specific IP block that our source IP belongs to, as well as which country that IP block is from. The LPM (Longest prefix match) algorithm is best for this. In addition, eBPF supports the BPF_MAP_TYPE_LPM_TRIE map type, which uses this algorithm.
We have assigned each country code an ordinal number (ID), starting from zero, and added value pairs of IP addresses with IDs to the eBPF map. For instance, the IP addresses of New Zealand (NZ) are assigned ID 0. Those from Belgium (BE) are assigned 1:
126.96.36.199/24 → 0 188.8.131.52/32 → 1
We also added the custom policy for countries as a bit value on the map. The allow policy is 0 bits, and the blocking policy is 1 bit. So, for example, if we decide to block New Zealand (NZ), Dominica (DM), and Lithuania (LT), the bitmap will look as follows:
When using a bitmap, traffic filtering by GeoIP will be performed as follows:
We ran tests to evaluate how well the performance was maintained. The table below shows the results, including the overhead of our entire firewall pipeline. If you implement a BPF program with just GeoIP blocking, it’ll very likely be way faster.
As you can see, the smaller the packet size is, the higher the load on the firewall CPU is. At the same time, large packets can be filtered out with a high line rate.