
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
    <channel>
        <title><![CDATA[ The Cloudflare Blog ]]></title>
        <description><![CDATA[ Get the latest news on how products at Cloudflare are built, technologies used, and join the teams helping to build a better Internet. ]]></description>
        <link>https://blog.cloudflare.com</link>
        <atom:link href="https://blog.cloudflare.com/" rel="self" type="application/rss+xml"/>
        <language>en-us</language>
        <image>
            <url>https://blog.cloudflare.com/favicon.png</url>
            <title>The Cloudflare Blog</title>
            <link>https://blog.cloudflare.com</link>
        </image>
        <lastBuildDate>Wed, 15 Apr 2026 19:40:08 GMT</lastBuildDate>
        <item>
            <title><![CDATA[Introducing Programmable Flow Protection: custom DDoS mitigation logic for Magic Transit customers]]></title>
            <link>https://blog.cloudflare.com/programmable-flow-protection/</link>
            <pubDate>Tue, 31 Mar 2026 13:00:00 GMT</pubDate>
            <description><![CDATA[ Magic Transit customers can now program their own DDoS mitigation logic and deploy it across Cloudflare’s global network. This enables precise, stateful mitigation for custom and proprietary UDP protocols. ]]></description>
            <content:encoded><![CDATA[ <p>We're proud to introduce <a href="https://developers.cloudflare.com/ddos-protection/advanced-ddos-systems/overview/programmable-flow-protection/"><u>Programmable Flow Protection</u></a>: a system designed to let <a href="https://www.cloudflare.com/network-services/products/magic-transit/"><u>Magic Transit</u></a> customers implement their own custom DDoS mitigation logic and deploy it across Cloudflare’s global network. This enables precise, stateful mitigation for custom and proprietary protocols built on UDP. It is engineered to provide the highest possible level of customization and flexibility to mitigate DDoS attacks of any scale. </p><p>Programmable Flow Protection is currently in beta and available to all Magic Transit Enterprise customers for an additional cost. Contact your account team to join the beta or sign up at <a href="https://www.cloudflare.com/en-gb/lp/programmable-flow-protection/"><u>this page</u></a>.</p>
    <div>
      <h3>Programmable Flow Protection is customizable</h3>
      <a href="#programmable-flow-protection-is-customizable">
        
      </a>
    </div>
    <p>Our existing <a href="https://www.cloudflare.com/ddos/"><u>DDoS mitigation systems</u></a> have been designed to understand and protect popular, well-known protocols from DDoS attacks. For example, our <a href="https://developers.cloudflare.com/ddos-protection/advanced-ddos-systems/overview/advanced-tcp-protection/"><u>Advanced TCP Protection</u></a> system uses specific known characteristics about the TCP protocol to issue challenges and establish a client’s legitimacy. Similarly, our <a href="https://blog.cloudflare.com/advanced-dns-protection/"><u>Advanced DNS Protection</u></a> builds a per-customer profile of DNS queries to mitigate DNS attacks. Our generic DDoS mitigation platform also understands common patterns across a variety of other well known protocols, including NTP, RDP, SIP, and many others.</p><p>However, custom or proprietary UDP protocols have always been a challenge for Cloudflare’s DDoS mitigation systems because our systems do not have the relevant protocol knowledge to make intelligent decisions to pass or drop traffic. </p><p>Programmable Flow Protection addresses this gap. Now, customers can write their own <a href="https://ebpf.io/"><u>eBPF</u></a> program that defines what “good” and “bad” packets are and how to deal with them. Cloudflare then runs the program across our entire global network. The program can choose to either drop or challenge “bad” packets, preventing them from reaching the customer’s origin. </p>
    <div>
      <h3>The problem of UDP-based attacks</h3>
      <a href="#the-problem-of-udp-based-attacks">
        
      </a>
    </div>
    <p><a href="https://www.cloudflare.com/learning/ddos/glossary/user-datagram-protocol-udp/"><u>UDP</u></a> is a connectionless transport layer protocol. Unlike TCP, UDP has no handshake or stateful connections. It does not promise that packets will arrive in order or exactly once. UDP instead prioritizes speed and simplicity, and is therefore well-suited for online gaming, VoIP, video streaming, and any other use case where the application requires real-time communication between clients and servers.</p><p>Our DDoS mitigation systems have always been able to detect and mitigate attacks against well-known protocols built on top of UDP. For example, the standard DNS protocol is built on UDP, and each DNS packet has a well-known structure. If we see a DNS packet, we know how to interpret it. That makes it easier for us to detect and drop DNS-based attacks. </p><p>Unfortunately, if we don’t understand the protocol inside a UDP packet’s payload, our DDoS mitigation systems have limited options available at mitigation time. If an attacker <a href="https://www.cloudflare.com/learning/ddos/udp-flood-ddos-attack/"><u>sends a large flood of UDP traffic</u></a> that does not match any known patterns or protocols, Cloudflare can either entirely block or apply a rate limit to the destination IP and port combination. This is a crude “last line of defense” that is only intended to keep the rest of the customer’s network online, and it can be painful in a couple ways. </p><p>First, a block or a generic <a href="https://www.cloudflare.com/learning/bots/what-is-rate-limiting/"><u>rate limit</u></a> does not distinguish good traffic from bad, which means these mitigations will likely cause legitimate clients to experience lag or connection loss — doing the attacker’s job for them! Second, a generic rate limit can be too strict or too lax depending on the customer. For example, a customer who expects to receive 1Gbps of legitimate traffic probably needs more aggressive rate limiting compared to a customer who expects to receive 25Gbps of legitimate traffic.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/L8PZ6eWn9nkpATaNcUinB/b6c12b4be815fbd4e71166b6f0c30329/BLOG-3182_2.png" />
          </figure><p><sup><i>An illustration of UDP packet contents. A user can define a valid payload and reject traffic that doesn’t match the defined pattern.</i></sup></p><p>The Programmable Flow Protection platform was built to address this problem by allowing our customers to dictate what “good” versus “bad” traffic actually looks like. Many of our customers use custom or proprietary UDP protocols that we do not understand — and now we don’t have to.</p>
    <div>
      <h3>How Programmable Flow Protection works</h3>
      <a href="#how-programmable-flow-protection-works">
        
      </a>
    </div>
    <p>In previous blog posts, we’ve described how “flowtrackd”, our <a href="https://blog.cloudflare.com/announcing-flowtrackd/"><u>stateful network layer DDoS mitigation system</u></a>, protects Magic Transit users from complex TCP and DNS attacks. We’ve also described how we use Linux technologies like <a href="https://blog.cloudflare.com/l4drop-xdp-ebpf-based-ddos-mitigations/"><u>XDP</u></a> and <a href="https://blog.cloudflare.com/cloudflare-architecture-and-how-bpf-eats-the-world/"><u>eBPF</u></a> to efficiently mitigate common types of large scale DDoS attacks. </p><p>Programmable Flow Protection combines these technologies in a novel way. With Programmable Flow Protection, a customer can write their own eBPF program that decides whether to pass, drop, or challenge individual packets based on arbitrary logic. A customer can upload the program to Cloudflare, and Cloudflare will execute it on every packet destined to their network. Programs are executed in userspace, not kernel space, which allows Cloudflare the flexibility to support a variety of customers and use cases on the platform without compromising security. Programmable Flow Protection programs run after all of Cloudflare’s existing DDoS mitigations, so users still benefit from our standard security protections. </p><p>There are many similarities between an XDP eBPF program loaded into the Linux kernel and an eBPF program running on the Programmable Flow Protection platform. Both types of programs are compiled down to BPF bytecode. They are both run through a “verifier” to ensure memory safety and verify program termination. They are also executed in a fast, lightweight VM to provide isolation and stability.</p><p>However, eBPF programs loaded into the Linux kernel make use of many Linux-specific “helper functions” to integrate with the network stack, maintain state between program executions, and emit packets to network devices. Programmable Flow Protection offers the same functionality whenever a customer chooses, but with a different API tailored specifically to implement DDoS mitigations. For example, we’ve built helper functions to store state about clients between program executions, perform cryptographic validation, and emit challenge packets to clients. With these helper functions, a developer can use the power of the Cloudflare platform to protect their own network.</p>
    <div>
      <h3>Combining customer knowledge with Cloudflare’s network</h3>
      <a href="#combining-customer-knowledge-with-cloudflares-network">
        
      </a>
    </div>
    <p>Let’s step through an example to illustrate how a customer’s protocol-specific knowledge can be combined with Cloudflare’s network to create powerful mitigations.</p><p>Say a customer hosts an online gaming server on UDP port 207. The game engine uses a proprietary application header that is specific to the game. Cloudflare has no knowledge of the structure or contents of the application header. The customer gets hit by DDoS attacks that overwhelm the game server and players report lag in gameplay. The attack traffic comes from highly randomized source IPs and ports, and the payload data appears to be random as well. </p><p>To mitigate the attack, the customer can use their knowledge of the application header and deploy a Programmable Flow Protection program to check a packet’s validity. In this example, the application header contains a token that is unique to the gaming protocol. The customer can therefore write a program to extract the last byte of the token. The program passes all packets with the correct value present and drops all other traffic:</p>
            <pre><code>#include &lt;linux/ip.h&gt;
#include &lt;linux/udp.h&gt;
#include &lt;arpa/inet.h&gt;

#include "cf_ebpf_defs.h"
#include "cf_ebpf_helper.h"

// Custom application header
struct apphdr {
    uint8_t  version;
    uint16_t length;   // Length of the variable-length token
    uint8_t  token[0]; // Variable-length token
} __attribute__((packed));

uint64_t
cf_ebpf_main(void *state)
{
    struct cf_ebpf_generic_ctx *ctx = state;
    struct cf_ebpf_parsed_headers headers;
    struct cf_ebpf_packet_data *p;

    // Parse the packet headers with provided helper function
    if (parse_packet_data(ctx, &amp;p, &amp;headers) != 0) {
        return CF_EBPF_DROP;
    }

    // Drop packets not destined to port 207
    struct udphdr *udp_hdr = (struct udphdr *)headers.udp;
    if (ntohs(udp_hdr-&gt;dest) != 207) {
        return CF_EBPF_DROP;
    }

    // Get application header from UDP payload
    struct apphdr *app = (struct apphdr *)(udp_hdr + 1);
    if ((uint8_t *)(app + 1) &gt; headers.data_end) {
        return CF_EBPF_DROP;
    }

    // Perform memory checks to satisfy the verifier
    // and access the token safely
    if ((uint8_t *)(app-&gt;token + token_len) &gt; headers.data_end) {
        return CF_EBPF_DROP;
    }

    // Check the last byte of the token against expected value
    uint8_t *last_byte = app-&gt;token + token_len - 1;
    if (*last_byte != 0xCF) {
        return CF_EBPF_DROP;
    }

    return CF_EBPF_PASS;
}</code></pre>
            <p><sup><i>An eBPF program to filter packets according to a value in the application header.</i></sup></p><p>This program leverages application-specific information to create a more targeted mitigation than Cloudflare is capable of crafting on its own. <b>Customers can now combine their proprietary knowledge with the capacity of Cloudflare’s global network to absorb and mitigate massive attacks better than ever before.</b></p>
    <div>
      <h3>Going beyond firewalls: stateful tracking and challenges</h3>
      <a href="#going-beyond-firewalls-stateful-tracking-and-challenges">
        
      </a>
    </div>
    <p>Many pattern checks, like the one performed in the example above, can be accomplished with traditional firewalls. However, programs provide useful primitives that are not available in firewalls, including variables, conditional execution, loops, and procedure calls. But what really sets Programmable Flow Protection apart from other solutions is its ability to statefully track flows and challenge clients to prove they are real. A common type of attack that showcases these abilities is a <i>replay attack</i>.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6Pgo9uUQDY1GTrxAOAOgiK/52c6d6a329cce05ff11ba3e4694313b2/BLOG-3182_3.png" />
          </figure><p>In a replay attack, an attacker repeatedly sends packets that were valid at <i>some</i> point, and therefore conform to expected patterns of the traffic, but are no longer valid in the application’s current context. For example, the attacker could record some of their valid gameplay traffic and use a script to duplicate and transmit the same traffic at a very high rate.</p><p>With Programmable Flow Protection, a user can deploy a program that challenges suspicious clients and drops scripted traffic. We can extend our original example as follows:</p>
            <pre><code>
#include &lt;linux/ip.h&gt;
#include &lt;linux/udp.h&gt;
#include &lt;arpa/inet.h&gt;

#include "cf_ebpf_defs.h"
#include "cf_ebpf_helper.h"

uint64_t
cf_ebpf_main(void *state)
{
    // ...
 
    // Get the status of this source IP (statefully tracked)
    uint8_t status;
    if (cf_ebpf_get_source_ip_status(&amp;status) != 0) {
        return CF_EBPF_DROP;
    }

    switch (status) {
        case NONE:
		// Issue a custom challenge to this source IP
             issue_challenge();
             cf_ebpf_set_source_ip_status(CHALLENGED);
             return CF_EBPF_DROP;


        case CHALLENGED:
		// Check if this packet passes the challenge
		// with custom logic
             if (verify_challenge()) {
                 cf_ebpf_set_source_ip_status(VERIFIED);
                 return CF_EBPF_PASS;
             } else {
                 cf_ebpf_set_source_ip_status(BLOCKED);
                 return CF_EBPF_DROP;
             }


        case VERIFIED:
		// This source IP has passed the challenge
		return CF_EBPF_PASS;

	 case BLOCKED:
		// This source IP has been blocked
		return CF_EBPF_DROP;

        default:
            return CF_EBPF_PASS;
    }


    return CF_EBPF_PASS;
}
</code></pre>
            <p><sup><i>An eBPF program to challenge UDP connections and statefully manage connections. This example has been simplified for illustration purposes.</i></sup></p><p>The program statefully tracks the source IP addresses it has seen and emits a packet with a cryptographic challenge back to unknown clients. A legitimate client running a valid gaming client is able to correctly solve the challenge and respond with proof, but the attacker’s script is not. Traffic from the attacker is marked as “blocked” and subsequent packets are dropped.</p><p>With these new abilities, customers can statefully track flows and make sure only real, verified clients can send traffic to their origin servers. Although we have focused the example on gaming, the potential use cases for this technology extend to any UDP-based protocol.</p>
    <div>
      <h3>Get started today</h3>
      <a href="#get-started-today">
        
      </a>
    </div>
    <p>We’re excited to offer the Programmable Flow Protection feature to Magic Transit Enterprise customers. Talk to your account manager to learn more about how you can enable Programmable Flow Protection to help keep your infrastructure safe.</p><p>We’re still in active development of the platform, and we’re excited to see what our users build next. If you are not yet a Cloudflare customer, let us know if you’d like to protect your network with Cloudflare and Programmable Flow Protection by signing up at this page: <a href="https://www.cloudflare.com/lp/programmable-flow-protection/"><u>https://www.cloudflare.com/lp/programmable-flow-protection/</u></a>.</p> ]]></content:encoded>
            <category><![CDATA[Beta]]></category>
            <category><![CDATA[DDoS]]></category>
            <category><![CDATA[UDP]]></category>
            <category><![CDATA[eBPF]]></category>
            <category><![CDATA[Magic Transit]]></category>
            <category><![CDATA[Network Services]]></category>
            <guid isPermaLink="false">64lPEfE3ML34AycHER46Tz</guid>
            <dc:creator>Anita Tenjarla</dc:creator>
            <dc:creator>Alex Forster</dc:creator>
            <dc:creator>Cody Doucette</dc:creator>
            <dc:creator>Venus Xeon-Blonde</dc:creator>
        </item>
        <item>
            <title><![CDATA[A deep dive into BPF LPM trie performance and optimization]]></title>
            <link>https://blog.cloudflare.com/a-deep-dive-into-bpf-lpm-trie-performance-and-optimization/</link>
            <pubDate>Tue, 21 Oct 2025 13:00:00 GMT</pubDate>
            <description><![CDATA[ This post explores the performance of BPF LPM tries, a critical data structure used for IP matching.  ]]></description>
            <content:encoded><![CDATA[ <p>It started with a mysterious soft lockup message in production. A single, cryptic line that led us down a rabbit hole into the performance of one of the most fundamental data structures we use: the BPF LPM trie.</p><p>BPF trie maps (<a href="https://docs.ebpf.io/linux/map-type/BPF_MAP_TYPE_LPM_TRIE/">BPF_MAP_TYPE_LPM_TRIE</a>) are heavily used for things like IP and IP+Port matching when routing network packets, ensuring your request passes through the right services before returning a result. The performance of this data structure is critical for serving our customers, but the speed of the current implementation leaves a lot to be desired. We’ve run into several bottlenecks when storing millions of entries in BPF LPM trie maps, such as entry lookup times taking hundreds of milliseconds to complete and freeing maps locking up a CPU for over 10 seconds. For instance, BPF maps are used when evaluating Cloudflare’s <a href="https://www.cloudflare.com/network-services/products/magic-firewall/"><u>Magic Firewall</u></a> rules and these bottlenecks have even led to traffic packet loss for some customers.</p><p>This post gives a refresher of how tries and prefix matching work, benchmark results, and a list of the shortcomings of the current BPF LPM trie implementation.</p>
    <div>
      <h2>A brief recap of tries</h2>
      <a href="#a-brief-recap-of-tries">
        
      </a>
    </div>
    <p>If it’s been a while since you last looked at the trie data structure (or if you’ve never seen it before), a trie is a tree data structure (similar to a binary tree) that allows you to store and search for data for a given key and where each node stores some number of key bits.</p><p>Searches are performed by traversing a path, which essentially reconstructs the key from the traversal path, meaning nodes do not need to store their full key. This differs from a traditional binary search tree (BST) where the primary invariant is that the left child node has a key that is less than the current node and the right child has a key that is greater. BSTs require that each node store the full key so that a comparison can be made at each search step.</p><p>Here’s an example that shows how a BST might store values for the keys:</p><ul><li><p>ABC</p></li><li><p>ABCD</p></li><li><p>ABCDEFGH</p></li><li><p>DEF</p></li></ul>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1uXt5qwpyq7VzrqxXlHFLj/99677afd73a98b9ce04d30209065499f/image4.png" />
          </figure><p>In comparison, a trie for storing the same set of keys might look like this.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3TfFZmwekNAF18yWlOIVWh/58396a19e053bd1c02734a6a54eea18e/image8.png" />
          </figure><p>This way of splitting out bits is really memory-efficient when you have redundancy in your data, e.g. prefixes are common in your keys, because that shared data only requires a single set of nodes. It’s for this reason that tries are often used to efficiently store strings, e.g. dictionaries of words – storing the strings “ABC” and “ABCD” doesn’t require 3 bytes + 4 bytes (assuming ASCII), it only requires 3 bytes + 1 byte because “ABC” is shared by both (the exact number of bits required in the trie is implementation dependent).</p><p>Tries also allow more efficient searching. For instance, if you wanted to know whether the key “CAR” existed in the BST you are required to go to the right child of the root (the node with key “DEF”) and check its left child because this is where it would live if it existed. A trie is more efficient because it searches in prefix order. In this particular example, a trie knows at the root whether that key is in the trie or not.</p><p>This design makes tries perfectly suited for performing longest prefix matches and for working with IP routing using CIDR. CIDR was introduced to make more efficient use of the IP address space (no longer requiring that classes fall into 4 buckets of 8 bits) but comes with added complexity because now the network portion of an IP address can fall anywhere. Handling the CIDR scheme in IP routing tables requires matching on the longest (most specific) prefix in the table rather than performing a search for an exact match.</p><p>If searching a trie does a single-bit comparison at each node, that’s a binary trie. If searching compares more bits we call that a <b><i>multibit trie</i></b>. You can store anything you like in a trie, including IP and subnet addresses – it’s all just ones and zeroes.</p><p>Nodes in multibit tries use more memory than in binary tries, but since computers operate on multibit words anyhow, it’s more efficient from a microarchitecture perspective to use multibit tries because you can traverse through the bits faster, reducing the number of comparisons you need to make to search for your data. It’s a classic space vs time tradeoff.</p><p>There are other optimisations we can use with tries. The distribution of data that you store in a trie might not be uniform and there could be sparsely populated areas. For example, if you store the strings “A” and “BCDEFGHI” in a multibit trie, how many nodes do you expect to use? If you’re using ASCII, you could construct the binary trie with a root node and branch left for “A” or right for “B”. With 8-bit nodes, you’d need another 7 nodes to store “C”, “D”, “E”, “F”, “G”, “H", “I”.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/LO6izFC5e06dRf9ra2roC/167ba5c4128fcebacc7b7a8eab199ea5/image5.png" />
          </figure><p>Since there are no other strings in the trie, that’s pretty suboptimal. Once you hit the first level after matching on “B” you know there’s only one string in the trie with that prefix, and you can avoid creating all the other nodes by using <b><i>path compression</i></b>. Path compression replaces nodes “C”, “D”, “E” etc. with a single one such as “I”.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1ADY3lNtF7NIgfUX7bX9vY/828a14e155d6530a4dc8cf3286ce8cc3/image13.png" />
          </figure><p>If you traverse the tree and hit “I”, you still need to compare the search key with the bits you skipped (“CDEFGH”) to make sure your search key matches the string. Exactly how and where you store the skipped bits is implementation dependent – BPF LPM tries simply store the entire key in the leaf node. As your data becomes denser, path compression is less effective.</p><p>What if your data distribution is dense and, say, all the first 3 levels in a trie are fully populated? In that case you can use <b><i>level compression</i></b><i> </i>and replace all the nodes in those levels with a single node that has 2**3 children. This is how Level-Compressed Tries work which are used for <a href="https://vincent.bernat.ch/en/blog/2017-ipv4-route-lookup-linux">IP route lookup</a> in the Linux kernel (see <a href="https://elixir.bootlin.com/linux/v6.12.43/source/net/ipv4/fib_trie.c"><u>net/ipv4/fib_trie.c</u></a>).</p><p>There are other optimisations too, but this brief detour is sufficient for this post because the BPF LPM trie implementation in the kernel doesn’t fully use the three we just discussed.</p>
    <div>
      <h2>How fast are BPF LPM trie maps?</h2>
      <a href="#how-fast-are-bpf-lpm-trie-maps">
        
      </a>
    </div>
    <p>Here are some numbers from running <a href="https://lore.kernel.org/bpf/20250827140149.1001557-1-matt@readmodwrite.com/"><u>BPF selftests benchmark</u></a> on AMD EPYC 9684X 96-Core machines. Here the trie has 10K entries, a 32-bit prefix length, and an entry for every key in the range [0, 10K).</p><table><tr><td><p>Operation</p></td><td><p>Throughput</p></td><td><p>Stddev</p></td><td><p>Latency</p></td></tr><tr><td><p>lookup</p></td><td><p>7.423M ops/s</p></td><td><p>0.023M ops/s</p></td><td><p>134.710 ns/op</p></td></tr><tr><td><p>update</p></td><td><p>2.643M ops/s</p></td><td><p>0.015M ops/s</p></td><td><p>378.310 ns/op</p></td></tr><tr><td><p>delete</p></td><td><p>0.712M ops/s</p></td><td><p>0.008M ops/s</p></td><td><p>1405.152 ns/op</p></td></tr><tr><td><p>free</p></td><td><p>0.573K ops/s</p></td><td><p>0.574K ops/s</p></td><td><p>1.743 ms/op</p></td></tr></table><p>The time to free a BPF LPM trie with 10K entries is noticeably large. We recently ran into an issue where this took so long that it caused <a href="https://lore.kernel.org/lkml/20250616095532.47020-1-matt@readmodwrite.com/"><u>soft lockup messages</u></a> to spew in production.</p><p>This benchmark gives some idea of worst case behaviour. Since the keys are so densely populated, path compression is completely ineffective. In the next section, we explore the lookup operation to understand the bottlenecks involved.</p>
    <div>
      <h2>Why are BPF LPM tries slow?</h2>
      <a href="#why-are-bpf-lpm-tries-slow">
        
      </a>
    </div>
    <p>The LPM trie implementation in <a href="https://elixir.bootlin.com/linux/v6.12.43/source/kernel/bpf/lpm_trie.c"><u>kernel/bpf/lpm_trie.c</u></a> has a couple of the optimisations we discussed in the introduction. It is capable of multibit comparisons at leaf nodes, but since there are only two child pointers in each internal node, if your tree is densely populated with a lot of data that only differs by one bit, these multibit comparisons degrade into single bit comparisons.</p><p>Here’s an example. Suppose you store the numbers 0, 1, and 3 in a BPF LPM trie. You might hope that since these values fit in a single 32 or 64-bit machine word, you could use a single comparison to decide which next node to visit in the trie. But that’s only possible if your trie implementation has 3 child pointers in the current node (which, to be fair, most trie implementations do). In other words, you want to make a 3-way branching decision but since BPF LPM tries only have two children, you’re limited to a 2-way branch.</p><p>A diagram for this 2-child trie is given below.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1ciL2t6aMyJHR2FfX41rNk/365abe47cf384729408cf9b98c65c0be/image9.png" />
          </figure><p>The leaf nodes are shown in green with the key, as a binary string, in the center. Even though a single 8-bit comparison is more than capable of figuring out which node has that key, the BPF LPM trie implementation resorts to inserting intermediate nodes (blue) to inject 2-way branching decisions into your path traversal because its parent (the orange root node in this case) only has 2 children. Once you reach a leaf node, BPF LPM tries can perform a multibit comparison to check the key. If a node supported pointers to more children, the above trie could instead look like this, allowing a 3-way branch and reducing the lookup time.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/17VoWl8OY6tzcARKDKuSjS/b9200dbeddf13f101b7085a549742f95/image3.png" />
          </figure><p>This 2-child design impacts the height of the trie. In the worst case, a completely full trie essentially becomes a binary search tree with height log2(nr_entries) and the height of the trie impacts how many comparisons are required to search for a key.</p><p>The above trie also shows how BPF LPM tries implement a form of path compression – you only need to insert an intermediate node where you have two nodes whose keys differ by a single bit. If instead of 3, you insert a key of 15 (0b1111), this won’t change the layout of the trie; you still only need a single node at the right child of the root.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2ecfKSeoqN3bfBXmC9KHw5/3be952edea34d6b2cc867ba31ce14805/image12.png" />
          </figure><p>And finally, BPF LPM tries do not implement level compression. Again, this stems from the fact that nodes in the trie can only have 2 children. IP route tables tend to have many prefixes in common and you typically see densely packed tries at the upper levels which makes level compression very effective for tries containing IP routes.</p><p>Here’s a graph showing how the lookup throughput for LPM tries (measured in million ops/sec) degrades as the number of entries increases, from 1 entry up to 100K entries.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/33I92exrEZTcUWOjxaBOqY/fb1de551b06e3272c8670d0117d738fa/image2.png" />
          </figure><p>Once you reach 1 million entries, throughput is around 1.5 million ops/sec, and continues to fall as the number of entries increases.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4OhaAaI5Y2XJCofI9V39z/567a01b3335f29ef3b46ccdd74dc27e5/image1.png" />
          </figure><p>Why is this? Initially, this is because of the L1 dcache miss rate. All of those nodes that need to be traversed in the trie are potential cache miss opportunities.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5Gx4fOLKmhUKHegybQU7sl/4936239213f0061d5cbc2f5d6b63fde6/image11.png" />
          </figure><p>As you can see from the graph, L1 dcache miss rate remains relatively steady and yet the throughput continues to decline. At around 80K entries, dTLB miss rate becomes the bottleneck.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4Jy7aTN3Nyo2EsbSzw313n/d26871fa417ffe293adb47fe7f7dc56b/image7.png" />
          </figure><p>Because BPF LPM tries to dynamically allocate individual nodes from a freelist of kernel memory, these nodes can live at arbitrary addresses. Which means traversing a path through a trie almost certainly will incur cache misses and potentially dTLB misses. This gets worse as the number of entries, and height of the trie, increases.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6CB3MvSvSgH1T2eY7Xlei8/81ebe572592ca71529d79564a88993f0/image10.png" />
          </figure>
    <div>
      <h2>Where do we go from here?</h2>
      <a href="#where-do-we-go-from-here">
        
      </a>
    </div>
    <p>By understanding the current limitations of the BPF LPM trie, we can now work towards building a more performant and efficient solution for the future of the Internet.</p><p>We’ve already contributed these benchmarks to the upstream Linux kernel — but that’s only the start. We have plans to improve the performance of BPM LPM tries, particularly the lookup function which is heavily used for our workloads. This post covered a number of optimisations that are already used by the <a href="https://elixir.bootlin.com/linux/v6.12.43/source/net/ipv4/fib_trie.c"><u>net/ipv4/fib_trie.c</u></a> code, so a natural first step is to refactor that code so that a common Level Compressed trie implementation can be used. Expect future blog posts to explore this work in depth.</p><p>If you’re interested in looking at more performance numbers, <a href="https://wiki.cfdata.org/display/~jesper">Jesper Brouer</a> has recorded some here: <a href="https://github.com/xdp-project/xdp-project/blob/main/areas/bench/bench02_lpm-trie-lookup.org">https://github.com/xdp-project/xdp-project/blob/main/areas/bench/bench02_lpm-trie-lookup.org</a>.</p><h6><i>If the Linux kernel, performance, or optimising data structures excites you, </i><a href="https://www.cloudflare.com/en-gb/careers/jobs/?department=Engineering&amp;location=default"><i>our engineering teams are hiring</i></a><i>.</i></h6><p></p> ]]></content:encoded>
            <category><![CDATA[Deep Dive]]></category>
            <category><![CDATA[eBPF]]></category>
            <category><![CDATA[IPv4]]></category>
            <category><![CDATA[IPv6]]></category>
            <category><![CDATA[Linux]]></category>
            <category><![CDATA[Performance]]></category>
            <guid isPermaLink="false">2A4WHjTqyxprwUMPaZ6tfj</guid>
            <dc:creator>Matt Fleming</dc:creator>
            <dc:creator>Jesper Brouer</dc:creator>
        </item>
        <item>
            <title><![CDATA[A July 4 technical reading list]]></title>
            <link>https://blog.cloudflare.com/july-4-2022-reading-list/</link>
            <pubDate>Mon, 04 Jul 2022 12:55:08 GMT</pubDate>
            <description><![CDATA[ Here’s a short list of recent technical blog posts to give you something to read today ]]></description>
            <content:encoded><![CDATA[ 
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2S9gHqjdCaiGCiCTBkGt0P/3a2a26f413cb9a908a9112a858495a7e/image1-61.png" />
            
            </figure><p>Here’s a short list of recent technical blog posts to give you something to read today.</p>
    <div>
      <h3>Internet Explorer, we hardly knew ye</h3>
      <a href="#internet-explorer-we-hardly-knew-ye">
        
      </a>
    </div>
    <p>Microsoft has announced the end-of-life for the venerable Internet Explorer browser. Here <a href="/internet-explorer-retired/">we take a look</a> at the demise of IE and the rise of the Edge browser. And we investigate how many bots on the Internet continue to impersonate Internet Explorer versions that have long since been replaced.</p>
    <div>
      <h3>Live-patching security vulnerabilities inside the Linux kernel with eBPF Linux Security Module</h3>
      <a href="#live-patching-security-vulnerabilities-inside-the-linux-kernel-with-ebpf-linux-security-module">
        
      </a>
    </div>
    <p>Looking for something with a lot of technical detail? Look no further than <a href="/live-patch-security-vulnerabilities-with-ebpf-lsm/">this blog about live-patching</a> the Linux kernel using eBPF. Code, Makefiles and more within!</p>
    <div>
      <h3>Hertzbleed explained</h3>
      <a href="#hertzbleed-explained">
        
      </a>
    </div>
    <p>Feeling mathematical? Or just need a dose of CPU-level antics? Look no further than this <a href="/hertzbleed-explained/">deep explainer</a> about how CPU frequency scaling leads to a nasty side channel affecting cryptographic algorithms.</p>
    <div>
      <h3>Early Hints update: How Cloudflare, Google, and Shopify are working together to build a faster Internet for everyone</h3>
      <a href="#early-hints-update-how-cloudflare-google-and-shopify-are-working-together-to-build-a-faster-internet-for-everyone">
        
      </a>
    </div>
    <p>The HTTP standard for Early Hints shows a lot of promise. How much? In this blog post, we <a href="/early-hints-performance/">dig into data</a> about Early Hints in the real world and show how much faster the web is with it.</p>
    <div>
      <h3>Private Access Tokens: eliminating CAPTCHAs on iPhones and Macs with open standards</h3>
      <a href="#private-access-tokens-eliminating-captchas-on-iphones-and-macs-with-open-standards">
        
      </a>
    </div>
    <p>Dislike CAPTCHAs? Yes, us too. As part of our program to eliminate captures there’s a new standard: Private Access Tokens. This blog shows <a href="/eliminating-captchas-on-iphones-and-macs-using-new-standard/">how they work</a> and how they can be used to prove you’re human without saying who you are.</p>
    <div>
      <h3>Optimizing TCP for high WAN throughput while preserving low latency</h3>
      <a href="#optimizing-tcp-for-high-wan-throughput-while-preserving-low-latency">
        
      </a>
    </div>
    <p>Network nerd? Yeah, me too. Here’s a very <a href="/optimizing-tcp-for-high-throughput-and-low-latency/">in depth look</a> at how we tune TCP parameters for low latency and high throughput.</p><p>...<i>We protect </i><a href="https://www.cloudflare.com/network-services/"><i>entire corporate networks</i></a><i>, help customers build </i><a href="https://workers.cloudflare.com/"><i>Internet-scale applications efficiently</i></a><i>, accelerate any </i><a href="https://www.cloudflare.com/performance/accelerate-internet-applications/"><i>website or Internet application</i></a><i>, ward off </i><a href="https://www.cloudflare.com/ddos/"><i>DDoS attacks</i></a><i>, keep </i><a href="https://www.cloudflare.com/application-security/"><i>hackers at bay</i></a><i>, and can help you on </i><a href="https://www.cloudflare.com/products/zero-trust/"><i>your journey to Zero Trust</i></a><i>.</i></p><p><i>Visit </i><a href="https://1.1.1.1/"><i>1.1.1.1</i></a><i> from any device to get started with our free app that makes your Internet faster and safer.To learn more about our mission to help build a better Internet, start </i><a href="https://www.cloudflare.com/learning/what-is-cloudflare/"><i>here</i></a><i>. If you’re looking for a new career direction, check out </i><a href="http://cloudflare.com/careers"><i>our open positions</i></a><i>.</i></p> ]]></content:encoded>
            <category><![CDATA[Reading List]]></category>
            <category><![CDATA[Radar]]></category>
            <category><![CDATA[Linux]]></category>
            <category><![CDATA[TCP]]></category>
            <category><![CDATA[Hertzbleed]]></category>
            <category><![CDATA[eBPF]]></category>
            <guid isPermaLink="false">4ffQabh80U3V99Grzwc88g</guid>
            <dc:creator>John Graham-Cumming</dc:creator>
        </item>
        <item>
            <title><![CDATA[Production ready eBPF, or how we fixed the BSD socket API]]></title>
            <link>https://blog.cloudflare.com/tubular-fixing-the-socket-api-with-ebpf/</link>
            <pubDate>Thu, 17 Feb 2022 17:02:54 GMT</pubDate>
            <description><![CDATA[ We are open sourcing the production tooling we’ve built for the sk_lookup hook we contributed to the Linux kernel, called tubular ]]></description>
            <content:encoded><![CDATA[ 
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3qt80mUTCxJp6nLkenADAL/29eb329be3097752997f3ac4b9a00f25/tubular-1.png" />
            
            </figure><p>As we develop new products, we often push our operating system - Linux - beyond what is commonly possible. A common theme has been relying on <a href="https://ebpf.io/what-is-ebpf/">eBPF</a> to build technology that would otherwise have required modifying the kernel. For example, we’ve built <a href="/l4drop-xdp-ebpf-based-ddos-mitigations/">DDoS mitigation</a> and a <a href="/unimog-cloudflares-edge-load-balancer/">load balancer</a> and use it to <a href="/introducing-ebpf_exporter/">monitor our fleet of servers</a>.</p><p>This software usually consists of a small-ish eBPF program written in C, executed in the context of the kernel, and a larger user space component that loads the eBPF into the kernel and manages its lifecycle. We’ve found that the ratio of eBPF code to userspace code differs by an order of magnitude or more. We want to shed some light on the issues that a developer has to tackle when dealing with eBPF and present our solutions for building rock-solid production ready applications which contain eBPF.</p><p>For this purpose we are open sourcing the production tooling we’ve built for the <a href="https://www.kernel.org/doc/html/latest/bpf/prog_sk_lookup.html">sk_lookup hook</a> we contributed to the Linux kernel, called <b>tubular</b>. It exists because <a href="/its-crowded-in-here/">we’ve outgrown the BSD sockets API</a>. To deliver some products we need features that are just not possible using the standard API.</p><ul><li><p>Our services are available on millions of IPs.</p></li><li><p>Multiple services using the same port on different addresses have to coexist, e.g. <a href="https://1.1.1.1/">1.1.1.1</a> resolver and our authoritative DNS.</p></li><li><p>Our Spectrum product <a href="/how-we-built-spectrum/">needs to listen on all 2^16 ports</a>.</p></li></ul><p>The source code for tubular is at <a href="https://github.com/cloudflare/tubular">https://github.com/cloudflare/tubular</a>, and it allows you to do all the things mentioned above. Maybe the most interesting feature is that you can change the addresses of a service on the fly:</p><div></div>
<p></p>
    <div>
      <h2>How tubular works</h2>
      <a href="#how-tubular-works">
        
      </a>
    </div>
    <p><code>tubular</code> sits at a critical point in the Cloudflare stack, since it has to inspect every connection terminated by a server and decide which application should receive it.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6ZFfhU9ui5dR4KpbOcofqr/c61fde7a87d189167a3b72ca90b61e20/unnamed.png" />
            
            </figure><p>Failure to do so will drop or misdirect connections hundreds of times per second. So it has to be incredibly robust during day to day operations. We had the following goals for tubular:</p><ul><li><p><b>Releases must be unattended and happen online.</b> tubular runs on thousands of machines, so we can’t babysit the process or take servers out of production.</p></li><li><p><b>Releases must fail safely.</b> A failure in the process must leave the previous version of tubular running, otherwise we may drop connections.</p></li><li><p><b>Reduce the impact of (userspace) crashes.</b> When the inevitable bug comes along we want to minimise the blast radius.</p></li></ul><p>In the past we had built a proof-of-concept control plane for sk_lookup called <a href="https://github.com/majek/inet-tool">inet-tool</a>, which proved that we could get away without a persistent service managing the eBPF. Similarly, tubular has <code>tubectl</code>: short-lived invocations make the necessary changes and persisting state is handled by the kernel in the form of <a href="https://www.kernel.org/doc/html/latest/bpf/maps.html">eBPF maps</a>. Following this design gave us crash resiliency by default, but left us with the task of mapping the user interface we wanted to the tools available in the eBPF ecosystem.</p>
    <div>
      <h2>The tubular user interface</h2>
      <a href="#the-tubular-user-interface">
        
      </a>
    </div>
    <p>tubular consists of a BPF program that attaches to the sk_lookup hook in the kernel and userspace Go code which manages the BPF program. The <code>tubectl</code> command wraps both in a way that is easy to distribute.</p><p><code>tubectl</code> manages two kinds of objects: bindings and sockets. A binding encodes a rule against which an incoming packet is matched. A socket is a reference to a TCP or UDP socket that can accept new connections or packets.</p><p>Bindings and sockets are "glued" together via arbitrary strings called labels. Conceptually, a binding assigns a label to some traffic. The label is then used to find the correct socket.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3G1ZM7iKvkGurKMBYqonW5/f6612068b8fb404a8cc0af1d02108c43/unnamed--2-.png" />
            
            </figure>
    <div>
      <h3>Adding bindings</h3>
      <a href="#adding-bindings">
        
      </a>
    </div>
    <p>To create a binding that steers port 80 (aka HTTP) traffic destined for 127.0.0.1 to the label “foo” we use <code>tubectl bind</code>:</p>
            <pre><code>$ sudo tubectl bind "foo" tcp 127.0.0.1 80</code></pre>
            <p>Due to the power of sk_lookup we can have much more powerful constructs than the BSD API. For example, we can redirect connections to all IPs in 127.0.0.0/24 to a single socket:</p>
            <pre><code>$ sudo tubectl bind "bar" tcp 127.0.0.0/24 80</code></pre>
            <p>A side effect of this power is that it's possible to create bindings that "overlap":</p>
            <pre><code>1: tcp 127.0.0.1/32 80 -&gt; "foo"
2: tcp 127.0.0.0/24 80 -&gt; "bar"</code></pre>
            <p>The first binding says that HTTP traffic to localhost should go to “foo”, while the second asserts that HTTP traffic in the localhost subnet should go to “bar”. This creates a contradiction, which binding should we choose? tubular resolves this by defining precedence rules for bindings:</p><ol><li><p>A prefix with a longer mask is more specific, e.g. 127.0.0.1/32 wins over 127.0.0.0/24.</p></li><li><p>A port is more specific than the port wildcard, e.g. port 80 wins over "all ports" (0).</p></li></ol><p>Applying this to our example, HTTP traffic to all IPs in 127.0.0.0/24 will be directed to bar, except for 127.0.0.1 which goes to foo.</p>
    <div>
      <h3>Getting ahold of sockets</h3>
      <a href="#getting-ahold-of-sockets">
        
      </a>
    </div>
    <p><code>sk_lookup</code> needs a reference to a TCP or a UDP socket to redirect traffic to it. However, a socket is usually accessible only by the process which created it with the socket syscall. For example, an HTTP server creates a TCP listening socket bound to port 80. How can we gain access to the listening socket?</p><p>A fairly well known solution is to make processes cooperate by passing socket file descriptors via <a href="/know-your-scm_rights/">SCM_RIGHTS</a> messages to a tubular daemon. That daemon can then take the necessary steps to hook up the socket with <code>sk_lookup</code>. This approach has several drawbacks:</p><ol><li><p>Requires modifying processes to send SCM_RIGHTS</p></li><li><p>Requires a tubular daemon, which may crash</p></li></ol><p>There is another way of getting at sockets by using systemd, provided <a href="https://www.freedesktop.org/software/systemd/man/systemd.socket.html">socket activation</a> is used. It works by creating an additional service unit with the correct <a href="https://www.freedesktop.org/software/systemd/man/systemd.service.html#Sockets=">Sockets</a> setting. In other words: we can leverage systemd oneshot action executed on creation of a systemd socket service, registering the socket into tubular. For example:</p>
            <pre><code>[Unit]
Requisite=foo.socket

[Service]
Type=oneshot
Sockets=foo.socket
ExecStart=tubectl register "foo"</code></pre>
            <p>Since we can rely on systemd to execute <code>tubectl</code> at the correct times we don't need a daemon of any kind. However, the reality is that a lot of popular software doesn't use systemd socket activation. Dealing with systemd sockets is complicated and doesn't invite experimentation. Which brings us to the final trick: <a href="https://www.man7.org/linux/man-pages/man2/pidfd_getfd.2.html">pidfd_getfd</a>:</p><blockquote><p>The <code>pidfd_getfd()</code> system call allocates a new file descriptor in the calling process. This new file descriptor is a duplicate of an existing file descriptor, targetfd, in the process referred to by the PID file descriptor pidfd.</p></blockquote><p>We can use it to iterate all file descriptors of a foreign process, and pick the socket we are interested in. To return to our example, we can use the following command to find the TCP socket bound to 127.0.0.1 port 8080 in the httpd process and register it under the "foo" label:</p>
            <pre><code>$ sudo tubectl register-pid "foo" $(pidof httpd) tcp 127.0.0.1 8080</code></pre>
            <p>It's easy to wire this up using systemd's <a href="https://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStartPre=">ExecStartPost</a> if the need arises.</p>
            <pre><code>[Service]
Type=forking # or notify
ExecStart=/path/to/some/command
ExecStartPost=tubectl register-pid $MAINPID foo tcp 127.0.0.1 8080</code></pre>
            
    <div>
      <h2>Storing state in eBPF maps</h2>
      <a href="#storing-state-in-ebpf-maps">
        
      </a>
    </div>
    <p>As mentioned previously, tubular relies on the kernel to store state, using <a href="https://prototype-kernel.readthedocs.io/en/latest/bpf/ebpf_maps.html">BPF key / value data structures also known as maps</a>. Using the <a href="https://www.kernel.org/doc/html/latest/userspace-api/ebpf/syscall.html">BPF_OBJ_PIN syscall</a> we can persist them in /sys/fs/bpf:</p>
            <pre><code>/sys/fs/bpf/4026532024_dispatcher
├── bindings
├── destination_metrics
├── destinations
├── sockets
└── ...</code></pre>
            <p>The way the state is structured differs from how the command line interface presents it to users. Labels like “foo” are convenient for humans, but they are of variable length. Dealing with variable length data in BPF is cumbersome and slow, so the BPF program never references labels at all. Instead, the user space code allocates numeric IDs, which are then used in the BPF. Each ID represents a (<code>label</code>, <code>domain</code>, <code>protocol</code>) tuple, internally called <code>destination</code>.</p><p>For example, adding a binding for "foo" <code>tcp 127.0.0.1</code> ... allocates an ID for ("<code>foo</code>", <code>AF_INET</code>, <code>TCP</code>). Including domain and protocol in the destination allows simpler data structures in the BPF. Each allocation also tracks how many bindings reference a destination so that we can recycle unused IDs. This data is persisted into the destinations hash table, which is keyed by (Label, Domain, Protocol) and contains (ID, Count). Metrics for each destination are tracked in destination_metrics in the form of per-CPU counters.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5JI7BzZmFdOS5DO6n2cpjh/3bf1a320954e9de2e4b60e64e0a3b375/unnamed--1--5.png" />
            
            </figure><p><code>bindings</code> is a <a href="https://en.wikipedia.org/wiki/Trie">longest prefix match (LPM) trie</a> which stores a mapping from (<code>protocol</code>, <code>port</code>, <code>prefix</code>) to (<code>ID</code>, <code>prefix length</code>). The ID is used as a key to the sockets map which contains pointers to kernel socket structures. IDs are allocated in a way that makes them suitable as an array index, which allows using the simpler BPF sockmap (an array) instead of a socket hash table. The prefix length is duplicated in the value to work around shortcomings in the BPF API.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4FJXwiooeaLRbRriCrETia/656cc5c0f78ca393627335cec1064755/unnamed--3-.png" />
            
            </figure>
    <div>
      <h2>Encoding the precedence of bindings</h2>
      <a href="#encoding-the-precedence-of-bindings">
        
      </a>
    </div>
    <p>As discussed, bindings have a precedence associated with them. To repeat the earlier example:</p>
            <pre><code>1: tcp 127.0.0.1/32 80 -&gt; "foo"
2: tcp 127.0.0.0/24 80 -&gt; "bar"</code></pre>
            <p>The first binding should be matched before the second one. We need to encode this in the BPF somehow. One idea is to generate some code that executes the bindings in order of specificity, a technique we’ve used to great effect in <a href="/l4drop-xdp-ebpf-based-ddos-mitigations/">l4drop</a>:</p>
            <pre><code>1: if (mask(ip, 32) == 127.0.0.1) return "foo"
2: if (mask(ip, 24) == 127.0.0.0) return "bar"
...</code></pre>
            <p>This has the downside that the program gets longer the more bindings are added, which slows down execution. It's also difficult to introspect and debug such long programs. Instead, we use a specialised BPF longest prefix match (LPM) map to do the hard work. This allows inspecting the contents from user space to figure out which bindings are active, which is very difficult if we had compiled bindings into BPF. The LPM map uses a trie behind the scenes, so <a href="https://en.wikipedia.org/wiki/Trie#Searching">lookup has complexity proportional to the length of the key</a> instead of linear complexity for the “naive” solution.</p><p>However, using a map requires a trick for encoding the precedence of bindings into a key that we can look up. Here is a simplified version of this encoding, which ignores IPv6 and uses labels instead of IDs. To insert the binding <code>tcp 127.0.0.0/24 80</code> into a trie we first convert the IP address into a number.</p>
            <pre><code>127.0.0.0    = 0x7f 00 00 00</code></pre>
            <p>Since we're only interested in the first 24 bits of the address we, can write the whole prefix as</p>
            <pre><code>127.0.0.0/24 = 0x7f 00 00 ??</code></pre>
            <p>where “?” means that the value is not specified. We choose the number 0x01 to represent TCP and prepend it and the port number (80 decimal is 0x50 hex) to create the full key:</p>
            <pre><code>tcp 127.0.0.0/24 80 = 0x01 50 7f 00 00 ??</code></pre>
            <p>Converting <code>tcp 127.0.0.1/32 80</code> happens in exactly the same way. Once the converted values are inserted into the trie, the LPM trie conceptually contains the following keys and values.</p>
            <pre><code>LPM trie:
        0x01 50 7f 00 00 ?? = "bar"
        0x01 50 7f 00 00 01 = "foo"</code></pre>
            <p>To find the binding for a TCP packet destined for 127.0.0.1:80, we again encode a key and perform a lookup.</p>
            <pre><code>input:  0x01 50 7f 00 00 01   TCP packet to 127.0.0.1:80
---------------------------
LPM trie:
        0x01 50 7f 00 00 ?? = "bar"
           y  y  y  y  y
        0x01 50 7f 00 00 01 = "foo"
           y  y  y  y  y  y
---------------------------
result: "foo"

y = byte matches</code></pre>
            <p>The trie returns “foo” since its key shares the longest prefix with the input. Note that we stop comparing keys once we reach unspecified “?” bytes, but conceptually “bar” is still a valid result. The distinction becomes clear when looking up the binding for a TCP packet to 127.0.0.255:80.</p>
            <pre><code>input:  0x01 50 7f 00 00 ff   TCP packet to 127.0.0.255:80
---------------------------
LPM trie:
        0x01 50 7f 00 00 ?? = "bar"
           y  y  y  y  y
        0x01 50 7f 00 00 01 = "foo"
           y  y  y  y  y  n
---------------------------
result: "bar"

n = byte doesn't match</code></pre>
            <p>In this case "foo" is discarded since the last byte doesn't match the input. However, "bar" is returned since its last byte is unspecified and therefore considered to be a valid match.</p>
    <div>
      <h2>Observability with minimal privileges</h2>
      <a href="#observability-with-minimal-privileges">
        
      </a>
    </div>
    <p>Linux has the powerful ss tool (part of iproute2) available to inspect socket state:</p>
            <pre><code>$ ss -tl src 127.0.0.1
State      Recv-Q      Send-Q           Local Address:Port           Peer Address:Port
LISTEN     0           128                  127.0.0.1:ipp                 0.0.0.0:*</code></pre>
            <p>With tubular in the picture this output is not accurate anymore. <code>tubectl</code> bindings makes up for this shortcoming:</p>
            <pre><code>$ sudo tubectl bindings tcp 127.0.0.1
Bindings:
 protocol       prefix port label
      tcp 127.0.0.1/32   80   foo</code></pre>
            <p>Running this command requires super-user privileges, despite in theory being safe for any user to run. While this is acceptable for casual inspection by a human operator, it's a dealbreaker for observability via pull-based monitoring systems like Prometheus. The usual approach is to expose metrics via an HTTP server, which would have to run with elevated privileges and be accessible to the Prometheus server somehow. Instead, BPF gives us the tools to enable read-only access to tubular state with minimal privileges.</p><p>The key is to carefully set file ownership and mode for state in /sys/fs/bpf. Creating and opening files in /sys/fs/bpf uses <a href="https://www.kernel.org/doc/html/latest/userspace-api/ebpf/syscall.html#bpf-subcommand-reference">BPF_OBJ_PIN and BPF_OBJ_GET</a>. Calling BPF_OBJ_GET with BPF_F_RDONLY is roughly equivalent to open(O_RDONLY) and allows accessing state in a read-only fashion, provided the file permissions are correct. tubular gives the owner full access but restricts read-only access to the group:</p>
            <pre><code>$ sudo ls -l /sys/fs/bpf/4026532024_dispatcher | head -n 3
total 0
-rw-r----- 1 root root 0 Feb  2 13:19 bindings
-rw-r----- 1 root root 0 Feb  2 13:19 destination_metrics</code></pre>
            <p>It's easy to choose which user and group should own state when loading tubular:</p>
            <pre><code>$ sudo -u root -g tubular tubectl load
created dispatcher in /sys/fs/bpf/4026532024_dispatcher
loaded dispatcher into /proc/self/ns/net
$ sudo ls -l /sys/fs/bpf/4026532024_dispatcher | head -n 3
total 0
-rw-r----- 1 root tubular 0 Feb  2 13:42 bindings
-rw-r----- 1 root tubular 0 Feb  2 13:42 destination_metrics</code></pre>
            <p>There is one more obstacle, <a href="https://github.com/systemd/systemd/blob/b049b48c4b6e60c3cbec9d2884f90fd4e7013219/src/shared/mount-setup.c#L111-L112">systemd mounts /sys/fs/bpf</a> in a way that makes it inaccessible to anyone but root. Adding the executable bit to the directory fixes this.</p>
            <pre><code>$ sudo chmod -v o+x /sys/fs/bpf
mode of '/sys/fs/bpf' changed from 0700 (rwx------) to 0701 (rwx-----x)</code></pre>
            <p>Finally, we can export metrics without privileges:</p>
            <pre><code>$ sudo -u nobody -g tubular tubectl metrics 127.0.0.1 8080
Listening on 127.0.0.1:8080
^C</code></pre>
            <p>There is a caveat, unfortunately: truly unprivileged access requires unprivileged BPF to be enabled. Many distros have taken to disabling it via the unprivileged_bpf_disabled sysctl, in which case scraping metrics does require CAP_BPF.</p>
    <div>
      <h2>Safe releases</h2>
      <a href="#safe-releases">
        
      </a>
    </div>
    <p>tubular is distributed as a single binary, but really consists of two pieces of code with widely differing lifetimes. The BPF program is loaded into the kernel once and then may be active for weeks or months, until it is explicitly replaced. In fact, a reference to the program (and link, see below) is persisted into /sys/fs/bpf:</p>
            <pre><code>/sys/fs/bpf/4026532024_dispatcher
├── link
├── program
└── ...</code></pre>
            <p>The user space code is executed for seconds at a time and is replaced whenever the binary on disk changes. This means that user space has to be able to deal with an "old" BPF program in the kernel somehow. The simplest way to achieve this is to compare what is loaded into the kernel with the BPF shipped as part of tubectl. If the two don't match we return an error:</p>
            <pre><code>$ sudo tubectl bind foo tcp 127.0.0.1 80
Error: bind: can't open dispatcher: loaded program #158 has differing tag: "938c70b5a8956ff2" doesn't match "e007bfbbf37171f0"</code></pre>
            <p><code>tag</code> is the truncated hash of the instructions making up a BPF program, which the kernel makes available for every loaded program:</p>
            <pre><code>$ sudo bpftool prog list id 158
158: sk_lookup  name dispatcher  tag 938c70b5a8956ff2
...</code></pre>
            <p>By comparing the tag tubular asserts that it is dealing with a supported version of the BPF program. Of course, just returning an error isn't enough. There needs to be a way to update the kernel program so that it's once again safe to make changes. This is where the persisted link in /sys/fs/bpf comes into play. <code>bpf_links</code> are used to attach programs to various BPF hooks. "Enabling" a BPF program is a two-step process: first, load the BPF program, next attach it to a hook using a bpf_link. Afterwards the program will execute the next time the hook is executed. By updating the link we can change the program on the fly, in an atomic manner.</p>
            <pre><code>$ sudo tubectl upgrade
Upgraded dispatcher to 2022.1.0-dev, program ID #159
$ sudo bpftool prog list id 159
159: sk_lookup  name dispatcher  tag e007bfbbf37171f0
…
$ sudo tubectl bind foo tcp 127.0.0.1 80
bound foo#tcp:[127.0.0.1/32]:80</code></pre>
            <p>Behind the scenes the upgrade procedure is slightly more complicated, since we have to update the pinned program reference in addition to the link. We pin the new program into /sys/fs/bpf:</p>
            <pre><code>/sys/fs/bpf/4026532024_dispatcher
├── link
├── program
├── program-upgrade
└── ...</code></pre>
            <p>Once the link is updated we <a href="https://www.man7.org/linux/man-pages/man2/rename.2.html">atomically rename</a> program-upgrade to replace program. In the future we may be able to <a href="https://lkml.kernel.org/netdev/20211028094724.59043-5-lmb@cloudflare.com/t/">use RENAME_EXCHANGE</a> to make upgrades even safer.</p>
    <div>
      <h2>Preventing state corruption</h2>
      <a href="#preventing-state-corruption">
        
      </a>
    </div>
    <p>So far we’ve completely neglected the fact that multiple invocations of <code>tubectl</code> could modify the state in /sys/fs/bpf at the same time. It’s very hard to reason about what would happen in this case, so in general it’s best to prevent this from ever occurring. A common solution to this is <a href="https://gavv.github.io/articles/file-locks/#differing-features">advisory file locks</a>. Unfortunately it seems like BPF maps don't support locking.</p>
            <pre><code>$ sudo flock /sys/fs/bpf/4026532024_dispatcher/bindings echo works!
flock: cannot open lock file /sys/fs/bpf/4026532024_dispatcher/bindings: Input/output error</code></pre>
            <p>This led to a bit of head scratching on our part. Luckily it is possible to flock the directory instead of individual maps:</p>
            <pre><code>$ sudo flock --exclusive /sys/fs/bpf/foo echo works!
works!</code></pre>
            <p>Each <code>tubectl</code> invocation likewise invokes <a href="https://www.man7.org/linux/man-pages//man2/flock.2.html"><code>flock()</code></a>, thereby guaranteeing that only ever a single process is making changes.</p>
    <div>
      <h2>Conclusion</h2>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>tubular is in production at Cloudflare today and has simplified the deployment of <a href="https://www.cloudflare.com/products/cloudflare-spectrum/">Spectrum</a> and our <a href="https://www.cloudflare.com/dns/">authoritative DNS</a>. It allowed us to leave behind limitations of the BSD socket API. However, its most powerful feature is that <a href="https://research.cloudflare.com/publications/Fayed2021/">the addresses a service is available on can be changed on the fly</a>. In fact, we have built tooling that automates this process across our global network. Need to listen on another million IPs on thousands of machines? No problem, it’s just an HTTP POST away.</p><p><i>Interested in working on tubular and our L4 load balancer</i> <a href="/unimog-cloudflares-edge-load-balancer/"><i>unimog</i></a><i>? We are</i> <a href="https://boards.greenhouse.io/cloudflare/jobs/3232234?gh_jid=3232234"><i>hiring in our European offices</i></a><i>.</i></p> ]]></content:encoded>
            <category><![CDATA[eBPF]]></category>
            <category><![CDATA[Linux]]></category>
            <category><![CDATA[Go]]></category>
            <guid isPermaLink="false">7ofIShaWHxqlp4ZmHyNRs</guid>
            <dc:creator>Lorenz Bauer</dc:creator>
        </item>
        <item>
            <title><![CDATA[How We Used eBPF to Build Programmable Packet Filtering in Magic Firewall]]></title>
            <link>https://blog.cloudflare.com/programmable-packet-filtering-with-magic-firewall/</link>
            <pubDate>Mon, 06 Dec 2021 13:59:53 GMT</pubDate>
            <description><![CDATA[ By combining the power of eBPF and Nftables, Magic Firewall can mitigate sophisticated attacks on infrastructure by enforcing a positive security model. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Cloudflare actively protects services from sophisticated attacks day after day. For users of Magic Transit, <a href="https://www.cloudflare.com/ddos/">DDoS protection</a> detects and drops attacks, while <a href="https://www.cloudflare.com/magic-firewall/">Magic Firewall</a> allows custom packet-level rules, enabling customers to deprecate hardware firewall appliances and block malicious traffic at Cloudflare’s network. The types of attacks and sophistication of attacks continue to evolve, as recent DDoS and reflection <a href="/attacks-on-voip-providers/">attacks</a> <a href="/update-on-voip-attacks/">against</a> VoIP services targeting protocols such as <a href="https://en.wikipedia.org/wiki/Session_Initiation_Protocol">Session Initiation Protocol</a> (SIP) have shown. Fighting these attacks requires pushing the limits of packet filtering beyond what traditional firewalls are capable of. We did this by taking best of class technologies and combining them in new ways to turn Magic Firewall into a blazing fast, fully programmable firewall that can stand up to even the most sophisticated of attacks.</p>
    <div>
      <h3>Magical Walls of Fire</h3>
      <a href="#magical-walls-of-fire">
        
      </a>
    </div>
    <p><a href="/introducing-magic-firewall/">Magic Firewall</a> is a distributed stateless packet firewall built on Linux nftables. It runs on every server, in every Cloudflare data center around the world. To provide isolation and flexibility, each customer’s nftables rules are configured within their own Linux network namespace.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7IjaJVGQWr5ssJPteOj4DR/2a0a310c33b86840a752386253f555b9/image1-20.png" />
            
            </figure><p>This diagram shows the life of an example packet when using <a href="/magic-transit-network-functions/">Magic Transit</a>, which has Magic Firewall built in. First, packets go into the server and DDoS protections are applied, which drops attacks as early as possible. Next, the packet is routed into a customer-specific network namespace, which applies the nftables rules to the packets. After this, packets are routed back to the origin via a GRE tunnel. Magic Firewall users can construct firewall statements from a <a href="https://developers.cloudflare.com/magic-firewall">single API</a>, using a flexible <a href="https://github.com/cloudflare/wirefilter">Wirefilter syntax</a>. In addition, rules can be configured via the Cloudflare dashboard, using friendly UI drag and drop elements.</p><p>Magic Firewall provides a very powerful syntax for matching on various packet parameters, but it is also limited to the matches provided by nftables. While this is more than sufficient for many use cases, it does not provide enough flexibility to implement the advanced packet parsing and content matching we want. We needed more power.</p>
    <div>
      <h3>Hello eBPF, meet Nftables!</h3>
      <a href="#hello-ebpf-meet-nftables">
        
      </a>
    </div>
    <p>When looking to add more power to your Linux networking needs, Extended Berkeley Packet Filter (<a href="https://ebpf.io/">eBPF</a>) is a natural choice. With eBPF, you can insert packet processing programs that execute <i>in the kernel</i>, giving you the flexibility of familiar programming paradigms with the speed of in-kernel execution. Cloudflare <a href="/tag/ebpf/">loves eBPF</a> and this technology has been transformative in enabling many of our products. Naturally, we wanted to find a way to use eBPF to extend our use of nftables in Magic Firewall. This means being able to match, using an eBPF program within a table and chain as a rule. By doing this we can have our cake and eat it too, by keeping our existing infrastructure and code, and extending it further.</p><p>If nftables could leverage eBPF natively, this story would be much shorter; alas, we had to continue our quest. To get us started in our search, we know that iptables integrates with eBPF. For example, one can use iptables and a pinned eBPF program for dropping packets with the following command:</p>
            <pre><code>iptables -A INPUT -m bpf --object-pinned /sys/fs/bpf/match -j DROP</code></pre>
            <p>This clue helped to put us on the right path. Iptables uses the <a href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/netfilter/xt_bpf.c#n60">xt_bpf</a> extension to match on an eBPF program. This extension uses the BPF_PROG_TYPE_SOCKET_FILTER eBPF program type, which allows us to load the packet information from the socket buffer and return a value based on our code.</p><p>Since we know iptables can use eBPF, why not just use that? Magic Firewall currently leverages nftables, which is a great choice for our use case due to its flexibility in syntax and programmable interface. Thus, we need to find a way to use the xt_bpf extension with nftables.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4EnxwNmNsjU68l44Mf0M3J/8d5f3b9cfc9f74f1d398c2955925e0c0/image2-13.png" />
            
            </figure><p>This <a href="https://developers.redhat.com/blog/2020/08/18/iptables-the-two-variants-and-their-relationship-with-nftables#using_iptables_nft">diagram</a> helps explain the relationship between iptables, nftables and the kernel. The nftables API can be used by both the iptables and nft userspace programs, and can configure both xtables matches (including xt_bpf) and normal nftables matches.</p><p>This means that given the right API calls (netlink/netfilter messages), we can embed an xt_bpf match within an nftables rule. In order to do this, we need to understand which netfilter messages we need to send. By using tools such as strace, Wireshark, and especially using the <a href="https://github.com/torvalds/linux/blob/master/net/netfilter/xt_bpf.c">source</a> we were able to construct a message that could append an eBPF rule given a table and chain.</p>
            <pre><code>NFTA_RULE_TABLE table
NFTA_RULE_CHAIN chain
NFTA_RULE_EXPRESSIONS | NFTA_MATCH_NAME
	NFTA_LIST_ELEM | NLA_F_NESTED
	NFTA_EXPR_NAME "match"
		NLA_F_NESTED | NFTA_EXPR_DATA
		NFTA_MATCH_NAME "bpf"
		NFTA_MATCH_REV 1
		NFTA_MATCH_INFO ebpf_bytes	</code></pre>
            <p>The structure of the netlink/netfilter message to add an eBPF match should look like the above example. Of course, this message needs to be properly embedded and include a conditional step, such as a verdict, when there is a match. The next step was decoding the format of <code>ebpf_bytes</code> as shown in the example below.</p>
            <pre><code> struct xt_bpf_info_v1 {
	__u16 mode;
	__u16 bpf_program_num_elem;
	__s32 fd;
	union {
		struct sock_filter bpf_program[XT_BPF_MAX_NUM_INSTR];
		char path[XT_BPF_PATH_MAX];
	};
};</code></pre>
            <p>The bytes format can be found in the kernel header definition of <a href="https://git.netfilter.org/iptables/tree/include/linux/netfilter/xt_bpf.h#n27">struct xt_bpf_info_v1</a>. The code example above shows the relevant parts of the structure.</p><p>The xt_bpf module supports both raw bytecodes, as well as a path to a pinned ebpf program. The later mode is the technique we used to combine the ebpf program with nftables.</p><p>With this information we were able to write code that could create netlink messages and properly serialize any relevant data fields. This approach was just the first step, we are also looking into incorporating this into proper tooling instead of sending custom netfilter messages.</p>
    <div>
      <h3>Just Add eBPF</h3>
      <a href="#just-add-ebpf">
        
      </a>
    </div>
    <p>Now we needed to construct an eBPF program and load it into an existing nftables table and chain. Starting to use eBPF can be a bit daunting. Which program type do we want to use? How do we compile and load our eBPF program? We started this process by doing some exploration and research.</p><p>First we constructed an example program to try it out.</p>
            <pre><code>SEC("socket")
int filter(struct __sk_buff *skb) {
  /* get header */
  struct iphdr iph;
  if (bpf_skb_load_bytes(skb, 0, &amp;iph, sizeof(iph))) {
    return BPF_DROP;
  }

  /* read last 5 bytes in payload of udp */
  __u16 pkt_len = bswap_16(iph.tot_len);
  char data[5];
  if (bpf_skb_load_bytes(skb, pkt_len - sizeof(data), &amp;data, sizeof(data))) {
    return BPF_DROP;
  }

  /* only packets with the magic word at the end of the payload are allowed */
  const char SECRET_TOKEN[5] = "xyzzy";
  for (int i = 0; i &lt; sizeof(SECRET_TOKEN); i++) {
    if (SECRET_TOKEN[i] != data[i]) {
      return BPF_DROP;
    }
  }

  return BPF_OK;
}</code></pre>
            <p>The excerpt mentioned is an example of an eBPF program that only accepts packets that have a magic string at the end of the payload. This requires checking the total length of the packet to find where to start the search. For clarity, this example omits error checking and headers.</p><p>Once we had a program, the next step was integrating it into our tooling. We tried a few technologies to load the program, like BCC, libbpf, and we even created a custom loader. Ultimately, we ended up using <a href="https://github.com/cilium/ebpf/">cilium’s ebpf library</a>, since we are using Golang for our control-plane program and the library makes it easy to generate, embed and load eBPF programs.</p>
            <pre><code># nft list ruleset
table ip mfw {
	chain input {
		#match bpf pinned /sys/fs/bpf/mfw/match drop
	}
}</code></pre>
            <p>Once the program is compiled and pinned, we can add matches into nftables using netlink commands. Listing the ruleset shows the match is present. This is incredible! We are now able to deploy custom C programs to provide advanced matching inside a Magic Firewall ruleset!</p>
    <div>
      <h3>More Magic</h3>
      <a href="#more-magic">
        
      </a>
    </div>
    <p>With the addition of eBPF to our toolkit, Magic Firewall is an even more flexible and powerful way to protect your network from bad actors. We are now able to look deeper into packets and implement more complex matching logic than nftables alone could provide. Since our firewall is running as software on all Cloudflare servers, we can quickly iterate and update features.</p><p>One outcome of this project is SIP protection, which is currently in beta. That’s only the beginning. We are currently exploring using eBPF for protocol validations, advanced field matching, looking into payloads, and supporting even larger sets of IP lists.</p><p>We welcome your help here, too! If you have other use cases and ideas, please talk to your account team. If you find this technology interesting, come <a href="https://www.cloudflare.com/careers/">join our team</a>!</p> ]]></content:encoded>
            <category><![CDATA[CIO Week]]></category>
            <category><![CDATA[Magic Firewall]]></category>
            <category><![CDATA[Magic Transit]]></category>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[VoIP]]></category>
            <category><![CDATA[eBPF]]></category>
            <guid isPermaLink="false">1RUM6TLPNlUYMBqyfPL81y</guid>
            <dc:creator>Chris J Arges</dc:creator>
        </item>
        <item>
            <title><![CDATA[Raking the floods: my intern project using eBPF]]></title>
            <link>https://blog.cloudflare.com/building-rakelimit/</link>
            <pubDate>Fri, 18 Sep 2020 11:00:00 GMT</pubDate>
            <description><![CDATA[ SYN-cookies help mitigating SYN-floods for TCP, but how can we protect services from similar attacks that use UDP? We designed an algorithm and a library to fill this gap, and it’s open source! ]]></description>
            <content:encoded><![CDATA[ 
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2A1vrFJotzsKPMJxmJdOON/81b2292df9631836d592585149fbef25/rakelimit.jpg" />
            
            </figure><p>Cloudflare has sophisticated DDoS attack mitigation systems with multiple layers to provide defense in depth. Some of these layers analyse large-scale traffic patterns to detect and mitigate attacks. Other layers are more protocol- and application-specific, in order to stop attacks that might be hard to detect from overall traffic patterns. In some cases, the best place to detect and stop an attack is in the service itself.</p><p>During <a href="/cloudflare-doubling-size-of-2020-summer-intern-class/">my internship at Cloudflare</a> this summer, I’ve developed a new open-source framework to help UDP services protect themselves from attacks. This framework incorporates Cloudflare’s experience in running UDP-based services like Spectrum and the 1.1.1.1 resolver.</p>
    <div>
      <h3>Goals of the framework</h3>
      <a href="#goals-of-the-framework">
        
      </a>
    </div>
    <p>First of all, let's discuss what it actually means to protect an UDP service. We want to ensure that an attacker cannot drown out legitimate traffic. To achieve this we identify floods and limit them while leaving legitimate traffic untouched.</p><p>The idea to mitigate such attacks is straight forward: first identify a group of packets that is related to an attack, and then apply a rate limit on this group. Such groups are determined based on the attributes available to us in the packet, such as addresses and ports.</p><p>We then drop packets in the group. We only want to drop as much traffic as necessary to comply with our set rate limit. Completely ignoring a set of packets just because it is slightly above the rate limit is not an option, as it may contain legitimate traffic.</p><p>This ensures both that our service stays responsive but also that legitimate packets experience as little impact as possible.</p><p>While rate limiting is a somewhat straightforward procedure, determining groups is a bit harder, for a number of reasons.</p>
    <div>
      <h3>Finding needles in the haystack</h3>
      <a href="#finding-needles-in-the-haystack">
        
      </a>
    </div>
    <p>The problem in determining groups in packets is that we have barely any context. We consider four things as useful attributes as attack signatures: the source address and port as well as the destination address and port. While that already is not a lot, it gets worse: the source address and port may not even be accurate. Packets can be spoofed, in which case an attacker hides their own address. That means only keeping a rate per source address may not provide much value, as it could simply be spoofed.</p><p>But there is another problem: keeping one rate per address does not scale. When bringing IPv6 into the equation and its <a href="https://www.ripe.net/about-us/press-centre/understanding-ip-addressing#:~:text=For%20IPv4%2C%20this%20pool%20is,basic%20unit%20for%20storing%20information.">whopping address space</a> it becomes clear it’s not going to work.</p><p>To solve these issues we turned to the academic world and found what we were looking for, the problem of <i>Heavy Hitters.</i> <i>Heavy Hitters</i> are elements of a datastream that appear frequently, and can be expressed relative to the overall elements of the stream. We can define for example that an element is considered to be a <i>Heavy Hitter</i> if its frequency exceeds, say, 10% of the overall count. To do so we naively could suggest to simply maintain a counter per element, but due to the space limitations this will not scale. Instead probabilistic algorithms such as a <a href="http://dimacs.rutgers.edu/~graham/pubs/papers/cm-full.pdf">CountMin sketch</a> or the <a href="https://www.cse.ust.hk/~raywong/comp5331/References/EfficientComputationOfFrequentAndTop-kElementsInDataStreams.pdf">SpaceSaving algorithm</a> can be used. These provide an estimated count instead of a precise one, but are capable of doing this with constant memory requirements, and in our case we will just save rates into the CountMin sketch instead of counts. So no matter how many unique elements we have to track, the memory consumption is the same.</p><p>We now have a way of finding the needle in the haystack, and it does have constant memory requirements, solving our problem. However, reality isn’t that simple. What if an attack is not just originating from a single port but many? Or what if a reflection attack is hitting our service, resulting in random source addresses but a single source port? Maybe a full /24 subnet is sending us a flood? We can not just keep a rate per combination we see, as it would ignore all these patterns.</p>
    <div>
      <h3>Grouping the groups: How to organize packets</h3>
      <a href="#grouping-the-groups-how-to-organize-packets">
        
      </a>
    </div>
    <p>Luckily the academic world has us covered again, with the concept of <i>Hierarchical Heavy Hitters.</i> It extends the <i>Heavy Hitter</i> concept by using the underlying hierarchy in the elements of the stream. For example, an IP address can be naturally grouped into several subnets:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7IPwA0tN0c6pOFMK5B2AT8/9101b971c3a69277a79b03a473e936ec/0B429C7C-869C-4517-9B95-C90600943486.png" />
            
            </figure><p>In this case we defined that we consider the fully-specified address, the /24 subnet and the /0 wildcard. We start at the left with the fully specified address, and each step walking towards the top we consider less information from it. We call these less-specific addresses generalisations, and measure how specific a generalisation is by assigning a level. In our example, the address 192.0.2.123 is at level 0, while 192.0.2.0/24 is at level 1, etc.</p><p>If we want to create a structure which can hold this information for every packet, it could look like this:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5lqAfAxLDrXwIgtQJ1Hm0/b37c644442d0f0e91ef38725929cb5ef/F4B2AA4A-4868-4E56-891A-6C19E0CACBA4.png" />
            
            </figure><p>We maintain a CountMin-sketch per subnet and then apply Heavy Hitters. When a new packet arrives and we need to determine if it is allowed to pass we simply check the rates of the corresponding elements in every node. If no rate exceeds the rate limit that we set, e.g. 25 packets per second (<i>pps</i>), it is allowed to pass.</p><p>The structure could now keep track of a single attribute, but we would waste a lot of context around packets! So instead of letting it go to waste, we use the two-dimensional approach for addresses proposed in the paper <a href="https://arxiv.org/abs/1102.5540">Hierarchical Heavy Hitters with SpaceSaving algorithm</a>, and extend it further to also incorporate ports into our structure. Ports do not have a natural hierarchy such as addresses, so they can only be in two states: either <i>specified</i> (e.g. 8080) or <i>wildcard</i>.</p><p>Now our structure looks like this:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/EowltLQdppI0JmX9TtKFU/f20d98ae5469b518897e4d9c621f5ab9/5D94D469-B2F3-48CD-918C-202B8C426B19.png" />
            
            </figure><p>Now let’s talk about the algorithm we use to traverse the structure and determine if a packet should be allowed to pass. The paper <i>Hierarchical Heavy Hitters with SpaceSaving algorithm</i> provides two methods that can be used on the data structure: one that updates elements and increases their counters, and one that provides all elements that currently are <i>Heavy Hitters</i>. This is actually not necessary for our use-case, as we are only interested if the element, or packet, we are looking at right now would be a <i>Heavy Hitter</i> to decide if it can pass or not.</p><p>Secondly, our goal is to prevent any Heavy Hitters from passing, thus leaving the structure with no _Heavy Hitter_s whatsoever. This is a great property, as it allows us to simplify the algorithm substantially, and it looks like this:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4gXxGnRtWTU6jorXm6538i/4c9c62fc80edffcf2ac2a4b2e2918292/A41D5CB1-5C6B-4378-B496-7B38E22DE0F7.png" />
            
            </figure><p>As you may notice, we update every node of a level and maintain the maximum rate we see. After each level we calculate a probability that determines if a packet should be passed to the next level, based on the maximum rate we saw on that level and a set rate limit. Each node essentially filters the traffic for the following, less specific level.</p><p>I actually left out a small detail: a packet is not dropped if any rate exceeds the limit, but instead is kept with the probability <i>rate limit</i>/<i>maximum rate seen</i>. The reason is that if we just drop all packets if the rates exceed the limit, we would drop the whole traffic, not just a subset to make it comply with our set rate limit.</p><p>Since we now still update more specific nodes even if a node reaches a rate limit, the rate limit will converge towards the underlying pattern of the attack as much as possible. That means other traffic will be impacted as minimally as possible, and that with no manual intervention whatsoever!</p>
    <div>
      <h3>BPF to the rescue: building a Go library</h3>
      <a href="#bpf-to-the-rescue-building-a-go-library">
        
      </a>
    </div>
    <p>As we want to use this algorithm to mitigate floods, we need to spend as little computation and overhead as possible before we decide if a packet should be dropped or not. As so often, we looked into the BPF toolbox and found what we need: <i>Socketfilters</i>. As our colleague Marek put it: <a href="/cloudflare-architecture-and-how-bpf-eats-the-world/">“It seems, no matter the question - BPF is the answer.”</a>.</p><p><i>Socketfilters</i> are pieces of code that can be attached to a single socket and get executed before a packet will be passed from kernel to userspace. This is ideal for a number of reasons. First, when the kernel runs the socket filter code, it gives it all the information from the packet we need, and other mitigations such as firewalls have been executed. Second the code is executed <i>per socket</i>, so every application can activate it as needed, and also set appropriate rate limits. It may even use different rate limits for different sockets. The third reason is privileges: we do not need to be root to attach the code to a socket. We can execute code in the kernel as a normal user!</p><p>BPF also has a number of limitations which have been already covered on this blog in the past, so we will focus on one that’s specific to our project: floating-point numbers.</p><p>To calculate rates we need floating-point numbers to provide an accurate estimate. BPF, and the whole kernel for that matter, does not support these. Instead we implemented a fixed-point representation, which uses a part of the available bits for the fractional part of a rational number and the remaining bits for the integer part. This allows us to represent floats within a certain range, but there is a catch when doing arithmetic: while subtraction and addition of two fixed-points work well, multiplication and division requires double the number of bits to ensure there will not be any loss in precision. As we use 64 bits for our fixed-point values, there is no larger data type available to ensure this does not happen. Instead of calculating the result with exact precision, we convert one of the arguments into an integer. That results in the loss of the fractional part, but as we deal with large rates that does not pose any issue, and helps us to work around the bit limitation as intermediate results fit into the available 64 bits. Whenever fixed-point arithmetic is necessary the precision of intermediate results has to be carefully considered.</p><p>There are many more details to the implementation, but instead of covering every single detail in this blog post lets just look at the code.</p><p>We open sourced rakelimit over on Github at <a href="https://github.com/cloudflare/rakelimit">cloudflare/rakelimit</a>! It is a full-blown Go library that can be enabled on any UDP socket, and is easy to configure.</p><p>The development is still in early stages and this is a first prototype, but we are excited to continue and push the development with the community! And if you still can’t get enough, look at our talk from this year's <a href="https://linuxplumbersconf.org/event/7/contributions/677/">Linux Plumbers Conference</a>.</p> ]]></content:encoded>
            <category><![CDATA[eBPF]]></category>
            <category><![CDATA[Linux]]></category>
            <category><![CDATA[UDP]]></category>
            <category><![CDATA[Programming]]></category>
            <guid isPermaLink="false">5v73ZaARMTKhq3UGeDWoMn</guid>
            <dc:creator>Jonas Otten</dc:creator>
        </item>
        <item>
            <title><![CDATA[It's crowded in here!]]></title>
            <link>https://blog.cloudflare.com/its-crowded-in-here/</link>
            <pubDate>Sat, 12 Oct 2019 13:00:00 GMT</pubDate>
            <description><![CDATA[ We recently gave a presentation on Programming socket lookup with BPF at the Linux Plumbers Conference 2019 in Lisbon, Portugal. ]]></description>
            <content:encoded><![CDATA[ <p>We recently gave a presentation on <a href="https://linuxplumbersconf.org/event/4/contributions/487/">Programming socket lookup with BPF</a> at the Linux Plumbers Conference 2019 in Lisbon, Portugal. This blog post is a recap of the problem statement and proposed solution we presented.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3ODIBQvumtXQYscbSqjLFn/24257f7186eae5e62fcaf20a166490ea/birds_cable_wire.jpg" />
          </figure><p>CC0 Public Domain, <a href="https://pxhere.com/en/photo/1526517">PxHere</a></p><p>Our edge servers are crowded. We run more than a dozen public facing services, leaving aside the all internal ones that do the work behind the scenes.</p><p>Quick Quiz #1: How many can you name? We blogged about them! <a href="https://blog.cloudflare.com/its-crowded-in-here/#quiz-1">Jump to answer</a>.</p><p>These services are exposed on more than a million Anycast <a href="https://www.cloudflare.com/ips/">public IPv4 addresses</a> partitioned into 100+ network prefixes.</p><p>To keep things uniform every Cloudflare edge server runs all services and responds to every Anycast address. This allows us to make efficient use of the hardware by load-balancing traffic between all machines. We have shared the details of Cloudflare <a href="https://blog.cloudflare.com/no-scrubs-architecture-unmetered-mitigation/">edge</a><a href="https://blog.cloudflare.com/cloudflare-architecture-and-how-bpf-eats-the-world/"> architecture</a> on the blog before.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6MHwv0fusFTiEylSaWQeg3/dbbaa25e7093b0be4fa8ec6dbcd88bca/edge_data_center-1.png" />
          </figure><p>Granted not all services work on all the addresses but rather on a subset of them, covering one or several network prefixes.</p><p>So how do you set up your network services to listen on hundreds of IP addresses without driving the network stack over the edge? Cloudflare engineers have had to ask themselves this question more than once over the years, and the answer has changed as our edge evolved. This evolution forced us to look for creative ways to work with the <a href="https://en.wikipedia.org/wiki/Berkeley_sockets">Berkeley sockets API</a>, a POSIX standard for assigning a network address and a port number to your application. It has been quite a journey, and we are not done yet.</p>
    <div>
      <h2>When life is simple - one address, one socket</h2>
      <a href="#when-life-is-simple-one-address-one-socket">
        
      </a>
    </div>
    
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6aVlT1BbslJwQRzC0HnF0p/eb5b0651ba36da1010e1bd72b7c57a55/mapping_1_to_1-1.png" />
          </figure><p>The simplest kind of association between an (IP address, port number) and a service that we can imagine is one-to-one. A server responds to client requests on a single address, on a well known port. To set it up the application has to open one socket for each transport protocol (be it TCP or UDP) it wants to support. A network server like our <a href="https://www.cloudflare.com/dns/">authoritative DNS</a> would open up two sockets (one for UDP, one for TCP):</p><p>(192.0.2.1, 53/tcp) ⇨ ("auth-dns", pid=1001, fd=3)
(192.0.2.1, 53/udp) ⇨ ("auth-dns", pid=1001, fd=4)</p><p>To take it to Cloudflare scale, the service is likely to have to receive on at least a /20 network prefix, which is a range of IPs with 4096 addresses in it.</p><p>This translates to opening 4096 sockets for each transport protocol. Something that is not likely to go unnoticed when looking at <a href="http://man7.org/linux/man-pages/man8/ss.8.html">ss tool</a> output.</p><p></p><p>$ sudo ss -ulpn 'sport = 53'
State  Recv-Q Send-Q  Local Address:Port Peer Address:Port
…
UNCONN 0      0           192.0.2.40:53        0.0.0.0:*    users:(("auth-dns",pid=77556,fd=11076))
UNCONN 0      0           192.0.2.39:53        0.0.0.0:*    users:(("auth-dns",pid=77556,fd=11075))
UNCONN 0      0           192.0.2.38:53        0.0.0.0:*    users:(("auth-dns",pid=77556,fd=11074))
UNCONN 0      0           192.0.2.37:53        0.0.0.0:*    users:(("auth-dns",pid=77556,fd=11073))
UNCONN 0      0           192.0.2.36:53        0.0.0.0:*    users:(("auth-dns",pid=77556,fd=11072))
UNCONN 0      0           192.0.2.31:53        0.0.0.0:*    users:(("auth-dns",pid=77556,fd=11071))
…</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2v9WUgAYqxc0JyRM6llZCa/5c29ebd8a06ab9ded6dc0c483431b88d/lots_of_socks.jpg" />
          </figure><p>CC BY 2.0, Luca Nebuloni, <a href="https://flickr.com/photos/7897906@N06/20655224708">Flickr</a></p><p>The approach, while naive, has an advantage: when an IP from the range gets attacked with a UDP flood, the receive queues of sockets bound to the remaining IP addresses are not affected.</p>
    <div>
      <h2>Life can be easier - all addresses, one socket</h2>
      <a href="#life-can-be-easier-all-addresses-one-socket">
        
      </a>
    </div>
    
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4nfFyQt9C1FLIKO6wnmV4Q/90405ed4e16846e092ecd7e0d88d5110/mapping_inaddr_any-1.png" />
          </figure><p>It seems rather silly to create so many sockets for one service to receive traffic on a range of addresses. Not only that, the more listening sockets there are, the longer the chains in the socket lookup hash table. We have learned the hard way that going in this direction <a href="https://blog.cloudflare.com/revenge-listening-sockets/">can hurt packet processing latency</a>.</p><p>The sockets API comes with a big hammer that can make our life easier - the <code>INADDR_ANY</code> aka <code>0.0.0.0</code> wildcard address. With <code>INADDR_ANY</code> we can make a single socket receive on all addresses assigned to our host, specifying just the port.</p>
            <pre><code>s = socket(AF_INET, SOCK_STREAM, 0)
s.bind(('0.0.0.0', 12345))
s.listen(16)</code></pre>
            <p></p><p>Quick Quiz #2: Is there another way to bind a socket to all local addresses? <a href="https://blog.cloudflare.com/its-crowded-in-here/#quiz-2">Jump to answer</a>.</p><p>In other words, compared to the naive “one address, one socket” approach, <code>INADDR_ANY</code> allows us to have a single catch-all listening socket for the whole IP range on which we accept incoming connections.</p><p>On Linux this is possible thanks to a two-phase listening socket lookup, where it falls back to search for an <code>INADDR_ANY</code> socket if a more specific match has not been found.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1B0Td9aV8dFNmmOZ7Z5Ell/51b479c1ed0abae1d71038ab030dd98b/tcp_socket_lookup-1.png" />
          </figure><p>Another upside of binding to <code>0.0.0.0</code> is that our application doesn’t need to be aware of what addresses we have assigned to our host. We are also free to assign or remove the addresses after binding the listening socket. No need to reconfigure the service when its listening IP range changes.</p><p>On the other hand if our service should be listening on just <code>A.B.C.0/20</code> prefix, binding to all local addresses is more than we need. We might unintentionally expose an otherwise internal-only service to external traffic without a proper firewall or a socket filter in place.</p><p>Then there is the security angle. Since we now only have one socket, attacks attempting to flood any of the IPs assigned to our host on our service’s port, will hit the catch-all socket and its receive queue. While in such circumstances the Linux <a href="https://blog.cloudflare.com/syn-packet-handling-in-the-wild/">TCP stack has your back</a>, UDP needs special care or legitimate traffic might drown in the flood of dropped packets.</p><p>Possibly the biggest downside, though, is that a service listening on the wildcard <code>INADDR_ANY</code> address claims the port number exclusively for itself. Binding over the wildcard-listening socket with a specific IP and port fails miserably due to the address already being taken (<code>EADDRINUSE</code>).</p>
            <pre><code>bind(3, {sa_family=AF_INET, sin_port=htons(12345), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
bind(4, {sa_family=AF_INET, sin_port=htons(12345), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EADDRINUSE (Address already in use)
</code></pre>
            <p>Unless your service is UDP-only, setting the <code>SO_REUSEADDR</code> socket option, will not help you overcome this restriction. The only way out is to turn to <code>SO_REUSEPORT</code>, normally used to construct a load-balancing socket group. And that is only if you are lucky enough to run the port-conflicting services as the same user (UID). That is a story for another post.Quick Quiz #3: Does setting the <code>SO_REUSEADDR</code> socket option have any effect at all when there is bind conflict? <a href="https://blog.cloudflare.com/its-crowded-in-here/#quiz-3">Jump to answer</a>.</p>
    <div>
      <h2>Life gets real - one port, two services</h2>
      <a href="#life-gets-real-one-port-two-services">
        
      </a>
    </div>
    <p>As it happens, at the Cloudflare edge we do host services that share the same port number but otherwise respond to requests on non-overlapping IP ranges. A prominent example of such port-sharing is our <a href="https://blog.cloudflare.com/dns-resolver-1-1-1-1/">1.1.1.1</a> recursive DNS resolver running side-by-side with the<a href="https://www.cloudflare.com/dns/"> authoritative DNS service</a> that we offer to all customers.</p><p>Sadly the s<a href="http://man7.org/linux/man-pages/man2/bind.2.html">ockets API</a> doesn’t allow us to express a setup in which two services share a port and accept requests on disjoint IP ranges.</p><p>However, as Linux development history shows, any networking API limitation can be overcome by introducing a new <a href="https://github.com/torvalds/linux/blame/master/include/uapi/asm-generic/socket.h">socket option</a>, with sixty-something options available (and counting!).</p><p>Enter <code>SO_BINDTOPREFIX</code>.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1VW8uJH7XkJKqUt6HZXWiU/6995812a081001a31238f45c0455960b/mapping_bindtoprefix-1.png" />
          </figure><p>Back in 2016 we proposed <a href="https://lore.kernel.org/netdev/1458699966-3752-1-git-send-email-gilberto.bertin@gmail.com/">an extension to the Linux network stack</a>. It allowed services to constrain a wildcard-bound socket to an IP range belonging to a network prefix.</p>
            <pre><code># Service 1, 127.0.0.0/20, 1234/tcp
net1, plen1 = '127.0.0.0', 20
bindprefix1 = struct.pack('BBBBBxxx', *inet_aton(net1), plen1)

s1 = socket(AF_INET, SOCK_STREAM, 0)
s1.setsockopt(SOL_IP, IP_BINDTOPREFIX, bindprefix1)
s1.bind(('0.0.0.0', 1234))
s1.listen(1)

# Service 2, 127.0.16.0/20, 1234/tcp
net2, plen2 = '127.0.16.0', 20
bindprefix2 = struct.pack('BBBBBxxx', *inet_aton(net2), plen2)

s2 = socket(AF_INET, SOCK_STREAM, 0)
s2.setsockopt(SOL_IP, IP_BINDTOPREFIX, bindprefix2)
s2.bind(('0.0.0.0', 1234))
s2.listen(1)
</code></pre>
            <p>This mechanism has served us well since then. Unfortunately, it didn’t get accepted upstream due to being too specific to our use-case. Having no better alternative we ended up maintaining patches in our kernel to this day.</p>
    <div>
      <h2>Life gets complicated - all ports, one service</h2>
      <a href="#life-gets-complicated-all-ports-one-service">
        
      </a>
    </div>
    <p>Just when we thought we had things figured out, we were faced with a new challenge. How to build a service that accepts connections on any of the 65,535 ports? The ultimate reverse proxy, if you will, code named <a href="https://www.cloudflare.com/products/cloudflare-spectrum/">Spectrum</a>.The <code>bind</code> syscall offers very little flexibility when it comes to mapping a socket to a port number. You can either specify the number you want or let the network stack pick an unused one for you. There is no counterpart of <code>INADDR_ANY</code>, a wildcard value to select all ports (<code>INPORT_ANY</code>?).To achieve what we wanted, we had to turn to <a href="https://blog.cloudflare.com/how-we-built-spectrum/">TPROXY</a>, a <a href="https://www.kernel.org/doc/Documentation/networking/tproxy.txt">Netfilter / <code>iptables</code> extension</a> designed for intercepting remote-destined traffic on the forward path. However, we use it to steer local-destined packets, that is ones targeted to our host, to a catch-all-ports socket.</p>
            <pre><code>iptables -t mangle -I PREROUTING \
         -d 192.0.2.0/24 -p tcp \
         -j TPROXY --on-ip=127.0.0.1 --on-port=1234</code></pre>
            
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3n0QTqrm2m4ZTiqPtiiF9a/fbbd5a464721df6ae8017805e2d540dd/mapping_tproxy-1.png" />
          </figure><p>TPROXY-based setup comes at a price. For starters, your service needs elevated privileges to create a special catch-all socket (see the <a href="http://man7.org/linux/man-pages/man7/ip.7.html"><code>IP_TRANSPARENT</code> socket option</a>). Then you also have to understand and consider the subtle interactions between TPROXY and the receive path for your traffic profile, for example:</p><ul><li><p>does connection tracking register the flows redirected with TPROXY?</p></li><li><p>is listening socket contention during a SYN flood when using TPROXY a concern?</p></li><li><p>do other parts of the network stack, like XDP programs, need to know about TPROXY redirecting packets?</p></li></ul><p>These are some of the questions we needed to answer, and after running it in production for a while now, we have a good idea of what the consequences of using TPROXY are.That said, it would not come as a shock, if tomorrow we’d discovered something new about TPROXY. Due to its complexity we’ve always considered using it to steer local-destined traffic a <a href="https://blog.cloudflare.com/how-we-built-spectrum/">hack</a>, a use-case outside its intended application. No matter how well understood, a hack remains a hack.</p>
    <div>
      <h2>Can BPF make life easier?</h2>
      <a href="#can-bpf-make-life-easier">
        
      </a>
    </div>
    <p>Despite its complex nature TPROXY shows us something important. No matter what IP or port the listening socket is bound to, with a bit of support from the network stack, we can steer any connection to it. As long the application is ready to handle this situation, things work.</p><p>Quick Quiz #4: Are there really no problems with accepting any connection on any socket? <a href="https://blog.cloudflare.com/its-crowded-in-here/#quiz-4">Jump to answer</a>.</p><p>This is a really powerful concept. With a bunch of TPROXY rules, we can configure any mapping between (address, port) tuples and listening sockets.</p><p><b>? Idea #1:</b> A local-destined connection can be accepted by any listening socket.</p><p>We didn’t tell you the whole story before. When we published <code>SO_BINDTOPREFIX</code> patches, they did not just get rejected. <a href="https://meta.wikimedia.org/wiki/Cunningham%27s_Law">As sometimes happens</a> by posting the wrong answer, we got <a href="https://lore.kernel.org/netdev/1459261895.6473.176.camel@edumazet-glaptop3.roam.corp.google.com/">the right answer</a> to our problem</p><blockquote><p>❝BPF is absolutely the way to go here, as it allows for whatever user specified tweaks, like a list of destination subnetwork, or/and a list of source network, or the date/time of the day, or port knocking without netfilter, or … you name it.❞</p></blockquote><p><b>? Idea #2:</b> How we pick a listening socket can be tweaked with BPF.</p><p>Combine the two ideas together, and we arrive at an exciting concept. Let’s run BPF code to match an incoming packet with a listening socket, ignoring the address the socket is bound to. ?</p><p>Here’s an example to illustrate it.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5zhLPX1wssfv3c4QvObgz9/66812e76990de528517d020dbca70638/idea_program_socket_lookup_with_bpf-2.png" />
          </figure><p>All packets coming on <code>192.0.2.0/24</code> prefix, port <code>53</code> are steered to socket <code>sk:2</code>, while traffic targeted at <code>203.0.113.1</code>, on any port number lands in socket <code>sk:4</code>.</p>
    <div>
      <h2>Welcome BPF inet_lookup</h2>
      <a href="#welcome-bpf-inet_lookup">
        
      </a>
    </div>
    
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4Pg5Lou5IFLuZcFHbaY46O/590e5267f496e9ce2e1eb6d1460bbd45/bpf_inet_lookup_hook-1.png" />
          </figure><p>To make this concept a reality we are proposing a new mechanism to program the socket lookup with BPF. What is socket lookup? It’s a stage on the receive path where the transport layer searches for a socket to dispatch the packet to. The last possible moment to steer packets before they land in the selected socket receive queue. In there we attach a new type of BPF program called <code>inet_lookup</code>.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2yATE46s8d1no0gYCw6Asw/6cd9e449e7d6b0571a8c69ce9e4d9f33/tcp_socket_lookup_with_bpf-1.png" />
          </figure><p>If you recall, socket lookup in the Linux TCP stack is a <a href="https://elixir.bootlin.com/linux/v5.4-rc2/source/include/net/inet_hashtables.h#L329">two phase process</a>. First the kernel will try to find an established (connected) socket matching the packet 4-tuple. If there isn’t one, it will continue by looking for a listening socket using just the packet 2-tuple as key.</p><p>Our proposed extension allows users to program the second phase, the listening socket lookup. If present, a BPF program is allowed to choose a listening socket and terminate the lookup. Our program is also free to ignore the packet, in which case the kernel will continue to look for a listening socket as usual.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/bjoj0UUcthGLLGOSTbida/3e139697c9d897cd11aabbdf80946032/bpf_inet_lookup_operation-1.png" />
          </figure><p>How does this new type of BPF program operate? On input, as context, it gets handed a subset of information extracted from packet headers, including the packet 4-tuple. Based on the input the program accesses a BPF map containing references to listening sockets, and selects one to yield as the socket lookup result.</p><p>If we take a look at the corresponding BPF code, the program structure resembles a firewall rule. We have some match statements followed by an action.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2lhm5L9NH0AN8tnMh2cMpl/14656d47d0e81440617893422ac2140b/bpf_inet_lookup_code_sample-2.png" />
          </figure><p>You may notice that we don’t access the BPF map with sockets directly. Instead, we follow an established pattern in BPF called “map based redirection”, where a dedicated BPF helper accesses the map and carries out any steps necessary to redirect the packet.</p><p>We’ve skipped over one thing. Where does the BPF map of sockets come from? We create it ourselves and populate it with sockets. This is most easily done if your service uses systemd <a href="http://0pointer.de/blog/projects/socket-activation.html">socket activation</a>. systemd will let you associate more than one service unit with a socket unit, and both of the services will receive a file descriptor for the same socket. From there it’s just a matter of inserting the socket into the BPF map.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2ufZk0QnR4cOf061PvseTW/a7bdf0a77d655be88adc527cadc4a5bf/bpf_inet_lookup_socket_activation-1.png" />
          </figure>
    <div>
      <h2>Demo time!</h2>
      <a href="#demo-time">
        
      </a>
    </div>
    <p>This is not just a concept. We have already published a first working <a href="https://lore.kernel.org/bpf/20190828072250.29828-1-jakub@cloudflare.com/">set of patches for the kernel</a> together with ancillary <a href="https://github.com/majek/inet-tool">user-space tooling</a> to configure the socket lookup to your needs.</p><p>If you would like to see it in action, you are in luck. We’ve put together a demo that shows just how easily you can bind a network service to (i) a single port, (ii) all ports, or (iii) a network prefix. On-the-fly, without having to restart the service! There is a port scan running to prove it.</p><p>You can also bind to all-addresses-all-ports (<code>0.0.0.0/0</code>) because why not? Take that <code>INADDR_ANY</code>. All thanks to BPF superpowers.</p>
    <div>
      <h2>Summary</h2>
      <a href="#summary">
        
      </a>
    </div>
    <p>We have gone over how the way we bind services to network addresses on the Cloudflare edge has evolved over time. Each approach has its pros and cons, summarized below. We are currently working on a new BPF-based mechanism for binding services to addresses, which is intended to address the shortcomings of existing solutions.</p><p><b>bind to one address and port
</b>? flood traffic on one address hits one socket, doesn’t affect the rest
? as many sockets as listening addresses, doesn’t scale</p><p><b>bind to all addresses with </b><b><code>INADDR_ANY
</code></b>? just one socket for all addresses, the kernel thanks you
? application doesn’t need to know about listening addresses
? flood scenario requires custom protection, at least for UDP
? port sharing is tricky or impossible</p><p><b>bind to a network prefix with </b><b><code>SO_BINDTOPREFIX
</code></b>? two services can share a port if their IP ranges are non-overlapping
? custom kernel API extension that never went upstream</p><p><b>bind to all port with TPROXY
</b>? enables redirecting all ports to a listening socket and more
? meant for intercepting forwarded traffic early on the ingress path
? has subtle interactions with the network stack
? requires privileges from the application</p><p><b>bind to anything you want with BPF </b><b><code>inet_lookup
</code></b>? allows for the same flexibility as with TPROXY or <code>SO_BINDTOPREFIX</code>
? services don’t need extra capabilities, meant for local traffic only
? needs cooperation from services or PID 1 to build a socket map</p><hr /><p>Getting to this point has been a team effort. A special thank you to Lorenz Bauer and <a href="https://blog.cloudflare.com/author/marek-majkowski/">Marek Majkowski</a> who have contributed in an essential way to the BPF <code>inet_lookup</code> implementation. The <code>SO_BINDTOPREFIX</code> patches were authored by <a href="https://blog.cloudflare.com/author/gilberto-bertin/">Gilberto Bertin</a>.Fancy joining the team? <a href="https://www.cloudflare.com/careers/departments/?utm_referrer=blog">Apply here!</a></p>
    <div>
      <h2>Quiz Answers</h2>
      <a href="#quiz-answers">
        
      </a>
    </div>
    
    <div>
      <h3>Quiz 1</h3>
      <a href="#quiz-1">
        
      </a>
    </div>
    <p>Q: How many Cloudflare services can you name?</p><ol><li><p><a href="https://www.cloudflare.com/cdn/">HTTP CDN</a> (tcp/80)</p></li><li><p><a href="https://www.cloudflare.com/ssl/">HTTPS CDN</a> (tcp/443, <a href="https://cloudflare-quic.com/">udp/443</a>)</p></li><li><p><a href="https://www.cloudflare.com/dns/">authoritative DNS</a> (udp/53)</p></li><li><p><a href="https://blog.cloudflare.com/dns-resolver-1-1-1-1/">recursive DNS</a> (udp/53, 853)</p></li><li><p><a href="https://blog.cloudflare.com/secure-time/">NTP with NTS</a> (udp/1234)</p></li><li><p><a href="https://blog.cloudflare.com/roughtime/">Roughtime time service</a> (udp/2002)</p></li><li><p><a href="https://blog.cloudflare.com/distributed-web-gateway/">IPFS Gateway</a> (tcp/443)</p></li><li><p><a href="https://blog.cloudflare.com/cloudflare-ethereum-gateway/">Ethereum Gateway</a> (tcp/443)</p></li><li><p><a href="https://blog.cloudflare.com/spectrum/">Spectrum proxy</a> (tcp/any, udp/any)</p></li><li><p><a href="https://blog.cloudflare.com/announcing-warp-plus/">WARP</a> (udp)</p></li></ol><p><a href="https://blog.cloudflare.com/its-crowded-in-here/#quiz-1-question">Go back</a></p>
    <div>
      <h3>Quiz 2</h3>
      <a href="#quiz-2">
        
      </a>
    </div>
    <p>Q: Is there another way to bind a socket to all local addresses?Yes, there is - by not <code>bind()</code>’ing it at all. Calling <code>listen()</code> on an unbound socket is equivalent to binding it to <code>INADDR_ANY</code> and letting the kernel pick a free port.$ strace -e socket,bind,listen nc -l
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
listen(3, 1)                            = 0
^Z
[1]+  Stopped                 strace -e socket,bind,listen nc -l
$ ss -4tlnp
State      Recv-Q Send-Q Local Address:Port               Peer Address:Port
LISTEN     0      1            *:42669      </p><p><a href="https://blog.cloudflare.com/its-crowded-in-here/#quiz-2-question">Go back</a></p>
    <div>
      <h3>Quiz 3</h3>
      <a href="#quiz-3">
        
      </a>
    </div>
    <p>Q: Does setting the <code>SO_REUSEADDR</code> socket option have any effect at all when there is bind conflict?Yes. If two processes are racing to <code>bind</code> and <code>listen</code> on the same TCP port, on an overlapping IP, setting <code>SO_REUSEADDR</code> changes which syscall will report an error (<code>EADDRINUSE</code>). Without <code>SO_REUSEADDR</code> it will always be the second bind. With <code>SO_REUSEADDR</code> set there is a window of opportunity for a second <code>bind</code> to succeed but the subsequent <code>listen</code> to fail.</p><p><a href="https://blog.cloudflare.com/its-crowded-in-here/#quiz-3-question">Go back</a></p>
    <div>
      <h3><b>Quiz 4</b></h3>
      <a href="#quiz-4">
        
      </a>
    </div>
    <p>Q: Are there really no problems with accepting any connection on any socket?If the connection is destined for an address assigned to our host, i.e. a local address, there are no problems. However, for remote-destined connections, sending return traffic from a non-local address (i.e., one not present on any interface) will not get past the Linux network stack. The <code>IP_TRANSPARENT</code> socket option bypasses this protection mechanism known as <a href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=88ef4a5a78e63420dd1dd770f1bd1dc198926b04">source address check</a> to lift this restriction.</p><p><a href="https://blog.cloudflare.com/its-crowded-in-here/#quiz-4-question">Go back</a></p> ]]></content:encoded>
            <category><![CDATA[eBPF]]></category>
            <category><![CDATA[Linux]]></category>
            <category><![CDATA[UDP]]></category>
            <guid isPermaLink="false">2tVUhaeVSAohZJbbDgK6vy</guid>
            <dc:creator>Jakub Sitnicki</dc:creator>
        </item>
        <item>
            <title><![CDATA[Cloudflare architecture and how BPF eats the world]]></title>
            <link>https://blog.cloudflare.com/cloudflare-architecture-and-how-bpf-eats-the-world/</link>
            <pubDate>Sat, 18 May 2019 15:00:00 GMT</pubDate>
            <description><![CDATA[ Recently at I gave a short talk titled "Linux at Cloudflare". The talk ended up being mostly about BPF. It seems, no matter the question - BPF is the answer.

Here is a transcript of a slightly adjusted version of that talk. ]]></description>
            <content:encoded><![CDATA[ <p>Recently at <a href="https://www.netdevconf.org/0x13/schedule.html">Netdev 0x13</a>, the Conference on Linux Networking in Prague, I gave <a href="https://netdevconf.org/0x13/session.html?panel-industry-perspectives">a short talk titled "Linux at Cloudflare"</a>. The <a href="https://speakerdeck.com/majek04/linux-at-cloudflare">talk</a> ended up being mostly about BPF. It seems, no matter the question - BPF is the answer.</p><p>Here is a transcript of a slightly adjusted version of that talk.</p><hr />
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1jvISZgDDA9AnXaBAJzYys/13b124e1f305c9ab4594db66f9123789/01_edge-network-locations-100.jpg" />
            
            </figure><p>At Cloudflare we run Linux on our servers. We operate two categories of data centers: large "Core" data centers, processing logs, analyzing attacks, computing analytics, and the "Edge" server fleet, delivering customer content from 180 locations across the world.</p><p>In this talk, we will focus on the "Edge" servers. It's here where we use the newest Linux features, optimize for performance and care deeply about DoS resilience.</p><hr />
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2L3Wbz94VfdflLpsTuIp9D/8b11fa614a4cc1134e4d4df466464425/image-9.png" />
            
            </figure><p>Our edge service is special due to our network configuration - we are extensively using anycast routing. Anycast means that the same set of IP addresses are announced by all our data centers.</p><p>This design has great advantages. First, it guarantees the optimal speed for end users. No matter where you are located, you will always reach the closest data center. Then, anycast helps us to spread out DoS traffic. During attacks each of the locations receives a small fraction of the total traffic, making it easier to ingest and filter out unwanted traffic.</p><hr />
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4xiYwbjJRCpt76iDITKowY/9800bb2637bb3f601ccbc7c6eedb6b36/03_edge-network-uniform-software-100-1.jpg" />
            
            </figure><p>Anycast allows us to keep the networking setup uniform across all edge data centers. We applied the same design inside our data centers - our software stack is uniform across the edge servers. All software pieces are running on all the servers.</p><p>In principle, every machine can handle every task - and we run many diverse and demanding tasks. We have a full HTTP stack, the magical <a href="https://www.cloudflare.com/developer-platform/workers/">Cloudflare Workers</a>, two sets of DNS servers - authoritative and resolver, and many other publicly facing applications like <a href="https://www.cloudflare.com/application-services/products/cloudflare-spectrum/">Spectrum</a> and <a href="https://www.cloudflare.com/learning/dns/what-is-1.1.1.1/">Warp</a>.</p><p>Even though every server has all the software running, requests typically cross many machines on their journey through the stack. For example, an HTTP request might be handled by a different machine during each of the 5 stages of the processing.</p><hr />
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/268deIgT4XLrgXfhRXxfx1/a5ced85738359d15ee16f9238ac759d8/image-23.png" />
            
            </figure><p>Let me walk you through the early stages of inbound packet processing:</p><p>(1) First, the packets hit our router. The router does ECMP, and forwards packets onto our Linux servers. We use ECMP to spread each target IP across many, at least 16, machines. This is used as a rudimentary load balancing technique.</p><p>(2) On the servers we ingest packets with XDP eBPF. In XDP we perform two stages. First, we run volumetric <a href="https://www.cloudflare.com/learning/ddos/ddos-mitigation/">DoS mitigations</a>, dropping packets belonging to very large layer 3 attacks.</p><p>(3) Then, still in XDP, we perform layer 4 <a href="https://www.cloudflare.com/learning/performance/what-is-load-balancing/">load balancing</a>. All the non-attack packets are redirected across the machines. This is used to work around the ECMP problems, gives us fine-granularity load balancing and allows us to gracefully take servers out of service.</p><p>(4) Following the redirection the packets reach a designated machine. At this point they are ingested by the normal Linux networking stack, go through the usual iptables firewall, and are dispatched to an appropriate network socket.</p><p>(5) Finally packets are received by an application. For example HTTP connections are handled by a "protocol" server, responsible for performing TLS encryption and processing HTTP, HTTP/2 and QUIC protocols.</p><p>It's in these early phases of request processing where we use the coolest new Linux features. We can group useful modern functionalities into three categories:</p><ul><li><p>DoS handling</p></li><li><p>Load balancing</p></li><li><p>Socket dispatch</p></li></ul><hr />
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3IlhL5q8CplwN5H1BY0ZZo/7f73d7d4c662113fac18404bcfac96c4/image-25.png" />
            
            </figure><p>Let's discuss DoS handling in more detail. As mentioned earlier, the first step after ECMP routing is Linux's XDP stack where, among other things, we run DoS mitigations.</p><p>Historically our mitigations for volumetric attacks were expressed in classic BPF and iptables-style grammar. Recently we adapted them to execute in the XDP eBPF context, which turned out to be surprisingly hard. Read on about our adventures:</p><ul><li><p><a href="/l4drop-xdp-ebpf-based-ddos-mitigations/">L4Drop: XDP DDoS Mitigations</a></p></li><li><p><a href="/xdpcap/">xdpcap: XDP Packet Capture</a></p></li><li><p><a href="https://netdevconf.org/0x13/session.html?talk-XDP-based-DDoS-mitigation">XDP based DoS mitigation</a> talk by Arthur Fabre</p></li><li><p><a href="https://netdevconf.org/2.1/papers/Gilberto_Bertin_XDP_in_practice.pdf">XDP in practice: integrating XDP into our DDoS mitigation pipeline</a> (PDF)</p></li></ul><p>During this project we encountered a number of eBPF/XDP limitations. One of them was the lack of concurrency primitives. It was very hard to implement things like race-free token buckets. Later we found that <a href="http://vger.kernel.org/lpc-bpf2018.html#session-9">Facebook engineer Julia Kartseva</a> had the same issues. In February this problem has been addressed with the introduction of <code>bpf_spin_lock</code> helper.</p><hr />
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2LAXf4Ayxh2sRR3hIsbBja/f8be1df5b19b10954b6a4ad092a177f7/image-26.png" />
            
            </figure><p>While our modern volumetric DoS defenses are done in XDP layer, we still rely on <code>iptables</code> for application layer 7 mitigations. Here, a higher level firewall’s features are useful: connlimit, hashlimits and ipsets. We also use the <code>xt_bpf</code> iptables module to run cBPF in iptables to match on packet payloads. We talked about this in the past:</p><ul><li><p><a href="https://speakerdeck.com/majek04/lessons-from-defending-the-indefensible">Lessons from defending the indefensible</a> (PPT)</p></li><li><p><a href="/introducing-the-bpf-tools/">Introducing the BPF tools</a></p></li></ul><hr />
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7MhIUEVsoocz1OSd8FKeYZ/a9e4321a070cd2d49800e978eb50b2d7/image-34.png" />
            
            </figure><p>After XDP and iptables, we have one final kernel side DoS defense layer.</p><p>Consider a situation when our UDP mitigations fail. In such case we might be left with a flood of packets hitting our application UDP socket. This might overflow the socket causing packet loss. This is problematic - both good and bad packets will be dropped indiscriminately. For applications like DNS it's catastrophic. In the past to reduce the harm, we ran one UDP socket per IP address. An unmitigated flood was bad, but at least it didn't affect the traffic to other server IP addresses.</p><p>Nowadays that architecture is no longer suitable. We are running more than 30,000 DNS IP's and running that number of UDP sockets is not optimal. Our modern solution is to run a single UDP socket with a complex eBPF socket filter on it - using the <code>SO_ATTACH_BPF</code> socket option. We talked about running eBPF on network sockets in past blog posts:</p><ul><li><p><a href="/epbf_sockets_hop_distance/">eBPF, Sockets, Hop Distance and manually writing eBPF assembly</a></p></li><li><p><a href="/sockmap-tcp-splicing-of-the-future/">SOCKMAP - TCP splicing of the future</a></p></li></ul><p>The mentioned eBPF rate limits the packets. It keeps the state - packet counts - in an eBPF map. We can be sure that a single flooded IP won't affect other traffic. This works well, though during work on this project we found a rather worrying bug in the eBPF verifier:</p><ul><li><p><a href="/ebpf-cant-count/">eBPF can't count?!</a></p></li></ul><p>I guess running eBPF on a UDP socket is not a common thing to do.</p><hr />
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/79bzCglYGH8Vb2hH6R9j39/39aa41a6e11b5cb0dc7f640cd7d7aa72/image-27.png" />
            
            </figure><p>Apart from the DoS, in XDP we also run a layer 4 load balancer layer. This is a new project, and we haven't talked much about it yet. Without getting into many details: in certain situations we need to perform a socket lookup from XDP.</p><p>The problem is relatively simple - our code needs to look up the "socket" kernel structure for a 5-tuple extracted from a packet. This is generally easy - there is a <code>bpf_sk_lookup</code> helper available for this. Unsurprisingly, there were some complications. One problem was the inability to verify if a received ACK packet was a valid part of a three-way handshake when SYN-cookies are enabled. My colleague Lorenz Bauer is working on adding support for this corner case.</p><hr />
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6j7ypFSgorN0lWBNY6Nc2m/b0ca29a08e3a1623a5f8c06c213f8d97/image-28.png" />
            
            </figure><p>After DoS and the load balancing layers, the packets are passed onto the usual Linux TCP / UDP stack. Here we do a socket dispatch - for example packets going to port 53 are passed onto a socket belonging to our DNS server.</p><p>We do our best to use vanilla Linux features, but things get complex when you use thousands of IP addresses on the servers.</p><p>Convincing Linux to route packets correctly is relatively easy with <a href="/how-we-built-spectrum">the "AnyIP" trick</a>. Ensuring packets are dispatched to the right application is another matter. Unfortunately, standard Linux socket dispatch logic is not flexible enough for our needs. For popular ports like TCP/80 we want to share the port between multiple applications, each handling it on a different IP range. Linux doesn't support this out of the box. You can call <code>bind()</code> either on a specific IP address or all IP's (with 0.0.0.0).</p><hr />
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2ERTm7jhXbKKfdo3CVmV81/2c8b5986539832c73971269a6bcf0964/image-29.png" />
            
            </figure><p>In order to fix this, we developed a custom kernel patch which adds <a href="http://patchwork.ozlabs.org/patch/602916/">a <code>SO_BINDTOPREFIX</code> socket option</a>. As the name suggests - it allows us to call <code>bind()</code> on a selected IP prefix. This solves the problem of multiple applications sharing popular ports like 53 or 80.</p><p>Then we run into another problem. For our Spectrum product we need to listen on all 65535 ports. Running so many listen sockets is not a good idea (see <a href="/revenge-listening-sockets/">our old war story blog</a>), so we had to find another way. After some experiments we learned to utilize an obscure iptables module - TPROXY - for this purpose. Read about it here:</p><ul><li><p><a href="/how-we-built-spectrum/">Abusing Linux's firewall: the hack that allowed us to build Spectrum</a></p></li></ul><p>This setup is working, but we don't like the extra firewall rules. We are working on solving this problem correctly - actually extending the socket dispatch logic. You guessed it - we want to extend socket dispatch logic by utilizing eBPF. Expect some patches from us.</p><hr />
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5eEog6Kt512U7uVmD5DVt8/d8216cc24e674f5f86b1d385bf93dfc5/image-32.png" />
            
            </figure><p>Then there is a way to use eBPF to improve applications. Recently we got excited about doing TCP splicing with SOCKMAP:</p><ul><li><p><a href="/sockmap-tcp-splicing-of-the-future/">SOCKMAP - TCP splicing of the future</a></p></li></ul><p>This technique has a great potential for improving tail latency across many pieces of our software stack. The current SOCKMAP implementation is not quite ready for prime time yet, but the potential is vast.</p><p>Similarly, the new <a href="https://netdevconf.org/2.2/papers/brakmo-tcpbpf-talk.pdf">TCP-BPF aka BPF_SOCK_OPS</a> hooks provide a great way of inspecting performance parameters of TCP flows. This functionality is super useful for our performance team.</p><hr />
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1MC8LLNDHLZ4wDYLcbbCHQ/3491b09f9b5eaf796457eb06781f8b98/12_prometheus-ebpf_exporter-100.jpg" />
            
            </figure><p>Some Linux features didn't age well and we need to work around them. For example, we are hitting limitations of networking metrics. Don't get me wrong - the networking metrics are awesome, but sadly they are not granular enough. Things like <code>TcpExtListenDrops</code> and <code>TcpExtListenOverflows</code> are reported as global counters, while we need to know it on a per-application basis.</p><p>Our solution is to use eBPF probes to extract the numbers directly from the kernel. My colleague Ivan Babrou wrote a Prometheus metrics exporter called "ebpf_exporter" to facilitate this. Read on:</p><ul><li><p><a href="/introducing-ebpf_exporter/">Introducing ebpf_exporter</a></p></li><li><p><a href="https://github.com/cloudflare/ebpf_exporter">https://github.com/cloudflare/ebpf_exporter</a></p></li></ul><p>With "ebpf_exporter" we can generate all manner of detailed metrics. It is very powerful and saved us on many occasions.</p><hr />
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1obSUWhzzUQxg8cmDqQ0Lr/15e6419a78802c4d19fc4d21bf161738/image-33.png" />
            
            </figure><p>In this talk we discussed 6 layers of BPFs running on our edge servers:</p><ul><li><p>Volumetric DoS mitigations are running on XDP eBPF</p></li><li><p>Iptables <code>xt_bpf</code> cBPF for application-layer attacks</p></li><li><p><code>SO_ATTACH_BPF</code> for rate limits on UDP sockets</p></li><li><p>Load balancer, running on XDP</p></li><li><p>eBPFs running application helpers like SOCKMAP for TCP socket splicing, and TCP-BPF for TCP measurements</p></li><li><p>"ebpf_exporter" for granular metrics</p></li></ul><p>And we're just getting started! Soon we will be doing more with eBPF based socket dispatch, eBPF running on <a href="https://linux.die.net/man/8/tc">Linux TC (Traffic Control)</a> layer and more integration with cgroup eBPF hooks. Then, our SRE team is maintaining ever-growing list of <a href="https://github.com/iovisor/bcc">BCC scripts</a> useful for debugging.</p><p>It feels like Linux stopped developing new API's and all the new features are implemented as eBPF hooks and helpers. This is fine and it has strong advantages. It's easier and safer to upgrade eBPF program than having to recompile a kernel module. Some things like TCP-BPF, exposing high-volume performance tracing data, would probably be impossible without eBPF.</p><p>Some say "software is eating the world", I would say that: "BPF is eating the software".</p> ]]></content:encoded>
            <category><![CDATA[eBPF]]></category>
            <category><![CDATA[Linux]]></category>
            <category><![CDATA[Programming]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Anycast]]></category>
            <category><![CDATA[TCP]]></category>
            <guid isPermaLink="false">5EXsVZKcFTNLXVbrgHx73a</guid>
            <dc:creator>Marek Majkowski</dc:creator>
        </item>
        <item>
            <title><![CDATA[eBPF can't count?!]]></title>
            <link>https://blog.cloudflare.com/ebpf-cant-count/</link>
            <pubDate>Fri, 03 May 2019 13:00:00 GMT</pubDate>
            <description><![CDATA[ It is unlikely we can tell you anything new about the extended Berkeley Packet Filter, eBPF for short, if you've read all the great man pages, docs, guides, and some of our blogs out there. But we can tell you a war story, who doesn't like those?  ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Grant mechanical calculating machine, public domain <a href="https://en.wikipedia.org/wiki/File:Grant_mechanical_calculating_machine_1877.jpg">image</a></p><p>It is unlikely we can tell you anything new about the extended Berkeley Packet Filter, eBPF for short, if you've read all the great <a href="http://man7.org/linux/man-pages/man2/bpf.2.html">man pages</a>, <a href="https://www.kernel.org/doc/Documentation/networking/filter.txt">docs</a>, <a href="https://cilium.readthedocs.io/en/latest/bpf/">guides</a>, and some of our <a href="https://blog.cloudflare.com/epbf_sockets_hop_distance/">blogs</a> out there.</p><p>But we can tell you a war story, and who doesn't like those? This one is about how eBPF lost its ability to count for a while<a href="#f1"><sup>1</sup></a>.</p><p>They say in our Austin, Texas office that all good stories start with "y'all ain't gonna believe this… tale." This one though, starts with a <a href="https://lore.kernel.org/netdev/CAJPywTJqP34cK20iLM5YmUMz9KXQOdu1-+BZrGMAGgLuBWz7fg@mail.gmail.com/">post</a> to Linux netdev mailing list from <a href="https://twitter.com/majek04">Marek Majkowski</a> after what I heard was a long night:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2z8UE9I87Kpw8ONzxBH3WW/96500c2ee678248af9c22936bb49cdee/ebpf_bug_email_netdev.png" />
            
            </figure><p>Marek's findings were quite shocking - if you subtract two 64-bit timestamps in eBPF, the result is garbage. But only when running as an unprivileged user. From root all works fine. Huh.</p><p>If you've seen Marek's <a href="https://speakerdeck.com/majek04/linux-at-cloudflare">presentation</a> from the Netdev 0x13 conference, you know that we are using BPF socket filters as one of the defenses against simple, volumetric DoS attacks. So potentially getting your packet count wrong could be a Bad Thing™, and affect legitimate traffic.</p><p>Let's try to reproduce this bug with a simplified <a href="https://github.com/cloudflare/cloudflare-blog/blob/master/2019-04-ebpf-alu32/bpf/filter.c#L63">eBPF socket filter</a> that subtracts two 64-bit unsigned integers passed to it from <a href="https://github.com/cloudflare/cloudflare-blog/blob/master/2019-04-ebpf-alu32/run_bpf.go#L93">user-space</a> though a BPF map. The input for our BPF program comes from a <a href="https://github.com/cloudflare/cloudflare-blog/blob/master/2019-04-ebpf-alu32/bpf/filter.c#L44">BPF array map</a>, so that the values we operate on are not known at build time. This allows for easy experimentation and prevents the compiler from optimizing out the operations.</p><p>Starting small, eBPF, what is 2 - 1? View the code <a href="https://github.com/cloudflare/cloudflare-blog/blob/master/2019-04-ebpf-alu32/run_bpf.go#L93">on our GitHub</a>.</p>
            <pre><code>$ ./run-bpf 2 1
arg0                    2 0x0000000000000002
arg1                    1 0x0000000000000001
diff                    1 0x0000000000000001</code></pre>
            <p>OK, eBPF, what is 2^32 - 1?</p>
            <pre><code>$ ./run-bpf $[2**32] 1
arg0           4294967296 0x0000000100000000
arg1                    1 0x0000000000000001
diff 18446744073709551615 0xffffffffffffffff</code></pre>
            <p>Wrong! But if we ask nicely with sudo:</p>
            <pre><code>$ sudo ./run-bpf $[2**32] 1
[sudo] password for jkbs:
arg0           4294967296 0x0000000100000000
arg1                    1 0x0000000000000001
diff           4294967295 0x00000000ffffffff</code></pre>
            
    <div>
      <h3>Who is messing with my eBPF?</h3>
      <a href="#who-is-messing-with-my-ebpf">
        
      </a>
    </div>
    <p>When computers stop subtracting, you know something big is up. We called for reinforcements.</p><p>Our colleague Arthur Fabre <a href="https://lore.kernel.org/netdev/20190301113901.29448-1-afabre@cloudflare.com/">quickly noticed</a> something is off when you examine the eBPF code loaded into the kernel. It turns out kernel doesn't actually run the eBPF it's supplied - it sometimes rewrites it first.</p><p>Any sane programmer would expect 64-bit subtraction to be expressed as <a href="https://github.com/cloudflare/cloudflare-blog/blob/master/2019-04-ebpf-alu32/bpf/filter.s#L47">a single eBPF instruction</a></p>
            <pre><code>$ llvm-objdump -S -no-show-raw-insn -section=socket1 bpf/filter.o
…
      20:       1f 76 00 00 00 00 00 00         r6 -= r7
…</code></pre>
            <p>However, that's not what the kernel actually runs. Apparently after the rewrite the subtraction becomes a complex, multi-step operation.</p><p>To see what the kernel is actually running we can use little known <a href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/bpf/bpftool?h=v5.0">bpftool utility</a>. First, we need to load our BPF</p>
            <pre><code>$ ./run-bpf --stop-after-load 2 1
[2]+  Stopped                 ./run-bpf 2 1</code></pre>
            <p>Then list all BPF programs loaded into the kernel with <code>bpftool prog list</code></p>
            <pre><code>$ sudo bpftool prog list
…
5951: socket_filter  name filter_alu64  tag 11186be60c0d0c0f  gpl
        loaded_at 2019-04-05T13:01:24+0200  uid 1000
        xlated 424B  jited 262B  memlock 4096B  map_ids 28786</code></pre>
            <p>The most recently loaded <code>socket_filter</code> must be our program (<code>filter_alu64</code>). Now we now know its id is 5951 and we can list its bytecode with</p>
            <pre><code>$ sudo bpftool prog dump xlated id 5951
…
  33: (79) r7 = *(u64 *)(r0 +0)
  34: (b4) (u32) r11 = (u32) -1
  35: (1f) r11 -= r6
  36: (4f) r11 |= r6
  37: (87) r11 = -r11
  38: (c7) r11 s&gt;&gt;= 63
  39: (5f) r6 &amp;= r11
  40: (1f) r6 -= r7
  41: (7b) *(u64 *)(r10 -16) = r6
…</code></pre>
            <p>bpftool can also display the JITed code with: <code>bpftool prog dump jited id 5951</code>.</p><p>As you see, subtraction is replaced with a series of opcodes. That is unless you are root. When running from root all is good</p>
            <pre><code>$ sudo ./run-bpf --stop-after-load 0 0
[1]+  Stopped                 sudo ./run-bpf --stop-after-load 0 0
$ sudo bpftool prog list | grep socket_filter
659: socket_filter  name filter_alu64  tag 9e7ffb08218476f3  gpl
$ sudo bpftool prog dump xlated id 659
…
  31: (79) r7 = *(u64 *)(r0 +0)
  32: (1f) r6 -= r7
  33: (7b) *(u64 *)(r10 -16) = r6
…</code></pre>
            <p>If you've spent any time using eBPF, you must have experienced first hand the dreaded eBPF verifier. It's a merciless judge of all eBPF code that will reject any programs that it deems not worthy of running in kernel-space.</p><p>What perhaps nobody has told you before, and what might come as a surprise, is that the very same verifier will actually also <a href="https://elixir.bootlin.com/linux/v4.20.13/source/kernel/bpf/verifier.c#L6421">rewrite and patch up your eBPF code</a> as needed to make it safe.</p><p>The problems with subtraction were introduced by an inconspicuous security fix to the verifier. The patch in question first landed in Linux 5.0 and was backported to 4.20.6 stable and 4.19.19 LTS kernel. <a href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=979d63d50c0c0f7bc537bf821e056cc9fe5abd38">The over 2000 words long commit message</a> doesn't spare you any details on the attack vector it targets.</p><p>The mitigation stems from <a href="https://nvd.nist.gov/vuln/detail/CVE-2019-7308">CVE-2019-7308</a> vulnerability <a href="https://bugs.chromium.org/p/project-zero/issues/detail?id=1711">discovered by Jann Horn at Project Zero</a>, which exploits pointer arithmetic, i.e. adding a scalar value to a pointer, to trigger speculative memory loads from out-of-bounds addresses. Such speculative loads change the CPU cache state and can be used to mount a <a href="https://googleprojectzero.blogspot.com/2018/01/reading-privileged-memory-with-side.html">Spectre variant 1 attack</a>.</p><p>To mitigate it the eBPF verifier rewrites any arithmetic operations on pointer values in such a way the result is always a memory location within bounds. The patch demonstrates how arithmetic operations on pointers get rewritten and we can spot a familiar pattern there</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/8irnzqpgIARBl3UdtS30S/5e885701ef2fe3a9426b1e960e48f0d1/bpf_commit.png" />
            
            </figure><p>Wait a minute… What pointer arithmetic? We are just trying to subtract two scalar values. How come the mitigation kicks in?</p><p>It shouldn't. It's a bug. The eBPF verifier keeps track of what kind of values the ALU is operating on, and in this corner case the state was ignored.</p><p>Why running BPF as root is fine, you ask? If your program has <code>CAP_SYS_ADMIN</code> privileges, side-channel mitigations <a href="https://elixir.bootlin.com/linux/v5.0/source/kernel/bpf/verifier.c#L7218">don't</a> <a href="https://elixir.bootlin.com/linux/v5.0/source/kernel/bpf/verifier.c#L3109">apply</a>. As root you already have access to kernel address space, so nothing new can leak through BPF.</p><p>After our report, <a href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=3612af783cf52c74a031a2f11b82247b2599d3cd">the fix</a> has quickly landed in v5.0 kernel and got backported to stable kernels 4.20.15 and 4.19.28. Kudos to Daniel Borkmann for getting the fix out fast. However, kernel upgrades are hard and in the meantime we were left with code running in production that was not doing what it was supposed to.</p>
    <div>
      <h3>32-bit ALU to the rescue</h3>
      <a href="#32-bit-alu-to-the-rescue">
        
      </a>
    </div>
    <p>As one of the eBPF maintainers <a href="https://lore.kernel.org/netdev/af0643e0-08a1-6326-2a80-71892de1bf56@iogearbox.net/">has pointed out</a>, 32-bit arithmetic operations are not affected by the verifier bug. This opens a door for a work-around.</p><p>eBPF registers, <code>r0</code>..<code>r10</code>, are 64-bits wide, but you can also access just the lower 32 bits, which are exposed as subregisters <code>w0</code>..<code>w10</code>. You can operate on the 32-bit subregisters using BPF ALU32 instruction subset. LLVM 7+ can generate eBPF code that uses this instruction subset. Of course, you need to you ask it nicely with trivial <code>-Xclang -target-feature -Xclang +alu32</code> toggle:</p>
            <pre><code>$ cat sub32.c
#include "common.h"

u32 sub32(u32 x, u32 y)
{
        return x - y;
}
$ clang -O2 -target bpf -Xclang -target-feature -Xclang +alu32 -c sub32.c
$ llvm-objdump -S -no-show-raw-insn sub32.o
…
sub32:
       0:       bc 10 00 00 00 00 00 00         w0 = w1
       1:       1c 20 00 00 00 00 00 00         w0 -= w2
       2:       95 00 00 00 00 00 00 00         exit</code></pre>
            <p>The <code>0x1c</code> <a href="https://elixir.bootlin.com/linux/v5.0/source/include/uapi/linux/bpf_common.h#L11">opcode</a> of the instruction #1, which can be broken down as <code>BPF_ALU | BPF_X | BPF_SUB</code> (read more in the <a href="https://www.kernel.org/doc/Documentation/networking/filter.txt">kernel docs</a>), is the 32-bit subtraction between registers we are looking for, as opposed to regular 64-bit subtract operation <code>0x1f = BPF_ALU64 | BPF_X | BPF_SUB</code>, which will get rewritten.</p><p>Armed with this knowledge we can borrow a page from <a href="https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic">bignum arithmetic</a> and subtract 64-bit numbers using just 32-bit ops:</p>
            <pre><code>u64 sub64(u64 x, u64 y)
{
        u32 xh, xl, yh, yl;
        u32 hi, lo;

        xl = x;
        yl = y;
        lo = xl - yl;

        xh = x &gt;&gt; 32;
        yh = y &gt;&gt; 32;
        hi = xh - yh - (lo &gt; xl); /* underflow? */

        return ((u64)hi &lt;&lt; 32) | (u64)lo;
}</code></pre>
            <p>This code compiles as expected on normal architectures, like x86-64 or ARM64, but BPF Clang target plays by its own rules:</p>
            <pre><code>$ clang -O2 -target bpf -Xclang -target-feature -Xclang +alu32 -c sub64.c -o - \
  | llvm-objdump -S -
…  
      13:       1f 40 00 00 00 00 00 00         r0 -= r4
      14:       1f 30 00 00 00 00 00 00         r0 -= r3
      15:       1f 21 00 00 00 00 00 00         r1 -= r2
      16:       67 00 00 00 20 00 00 00         r0 &lt;&lt;= 32
      17:       67 01 00 00 20 00 00 00         r1 &lt;&lt;= 32
      18:       77 01 00 00 20 00 00 00         r1 &gt;&gt;= 32
      19:       4f 10 00 00 00 00 00 00         r0 |= r1
      20:       95 00 00 00 00 00 00 00         exit</code></pre>
            <p>Apparently the compiler decided it was better to operate on 64-bit registers and discard the upper 32 bits. Thus we weren't able to get rid of the problematic <code>0x1f</code> opcode. Annoying, back to square one.</p>
    <div>
      <h3>Surely a bit of IR will do?</h3>
      <a href="#surely-a-bit-of-ir-will-do">
        
      </a>
    </div>
    <p>The problem was in Clang frontend - compiling C to IR. We know that BPF "assembly" backend for LLVM can generate bytecode that uses ALU32 instructions. Maybe if we tweak the Clang compiler's output just a little we can achieve what we want. This means we have to get our hands dirty with the LLVM Intermediate Representation (IR).</p><p>If you haven't heard of LLVM IR before, now is a good time to do some <a href="http://www.aosabook.org/en/llvm.html">reading</a><a href="#f2"><sup>2</sup></a>. In short the LLVM IR is what Clang produces and LLVM BPF backend consumes.</p><p>Time to write IR by hand! Here's a hand-tweaked <a href="https://github.com/cloudflare/cloudflare-blog/blob/master/2019-04-ebpf-alu32/bpf/sub64_ir.ll#L7">IR variant</a> of our <code>sub64()</code> function:</p>
            <pre><code>define dso_local i64 @sub64_ir(i64, i64) local_unnamed_addr #0 {
  %3 = trunc i64 %0 to i32      ; xl = (u32) x;
  %4 = trunc i64 %1 to i32      ; yl = (u32) y;
  %5 = sub i32 %3, %4           ; lo = xl - yl;
  %6 = zext i32 %5 to i64
  %7 = lshr i64 %0, 32          ; tmp1 = x &gt;&gt; 32;
  %8 = lshr i64 %1, 32          ; tmp2 = y &gt;&gt; 32;
  %9 = trunc i64 %7 to i32      ; xh = (u32) tmp1;
  %10 = trunc i64 %8 to i32     ; yh = (u32) tmp2;
  %11 = sub i32 %9, %10         ; hi = xh - yh
  %12 = icmp ult i32 %3, %5     ; tmp3 = xl &lt; lo
  %13 = zext i1 %12 to i32
  %14 = sub i32 %11, %13        ; hi -= tmp3
  %15 = zext i32 %14 to i64
  %16 = shl i64 %15, 32         ; tmp2 = hi &lt;&lt; 32
  %17 = or i64 %16, %6          ; res = tmp2 | (u64)lo
  ret i64 %17
}</code></pre>
            <p>It may not be pretty but it does produce <a href="https://github.com/cloudflare/cloudflare-blog/blob/master/2019-04-ebpf-alu32/bpf/sub64_ir.s#L5">desired BPF code</a> when compiled<a href="#f3"><sup>3</sup></a>. You will likely find the <a href="https://llvm.org/docs/LangRef.html">LLVM IR reference</a> helpful when deciphering it.</p><p>And voila! First working solution that produces correct results:</p>
            <pre><code>$ ./run-bpf -filter ir $[2**32] 1
arg0           4294967296 0x0000000100000000
arg1                    1 0x0000000000000001
diff           4294967295 0x00000000ffffffff</code></pre>
            <p>Actually using this hand-written IR function from C is tricky. See <a href="https://github.com/cloudflare/cloudflare-blog/blob/master/2019-04-ebpf-alu32/build.ninja#L27">our code on GitHub</a>.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6zQLSvXLH19lXCjYnstl2a/f8fb73710fc8e5eda96476006974eea5/LED_DISP-1.JPG.jpeg" />
            
            </figure><p>public domain <a href="https://en.wikipedia.org/wiki/File:LED_DISP.JPG">image</a> by <a href="https://commons.wikimedia.org/wiki/User:Sergei_Frolov">Sergei Frolov</a></p>
    <div>
      <h3>The final trick</h3>
      <a href="#the-final-trick">
        
      </a>
    </div>
    <p>Hand-written IR does the job. The downside is that linking IR modules to your C modules is hard. Fortunately there is a better way. You can persuade Clang to stick to 32-bit ALU ops in generated IR.</p><p>We've already seen the problem. To recap, if we ask Clang to subtract 32-bit integers, it will operate on 64-bit values and throw away the top 32-bits. Putting C, IR, and eBPF side-by-side helps visualize this:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/33bbylbcFR7zuF7NwxHcTs/fa9f110a71ab6e5f5cf8f7d70aadd587/sub32_v1.png" />
            
            </figure><p>The trick to get around it is to declare the 32-bit variable that holds the result as <code>volatile</code>. You might already know the <a href="https://en.wikipedia.org/wiki/Volatile_(computer_programming)"><code>volatile</code> keyword</a> if you've written Unix signal handlers. It basically tells the compiler that the value of the variable may change under its feet so it should refrain from reorganizing loads (reads) from it, as well as that stores (writes) to it might have side-effects so changing the order or eliminating them, by skipping writing it to the memory, is not allowed either.</p><p>Using <code>volatile</code> makes Clang emit <a href="https://llvm.org/docs/LangRef.html#volatile-memory-accesses">special loads and/or stores</a> at the IR level, which then on eBPF level translates to writing/reading the value from memory (stack) on every access. While this sounds not related to the problem at hand, there is a surprising side-effect to it:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2fEaCu15LmmeauGv7Vh1Wb/354c4a8aa60da7a7e5797fe0f951508d/sub32_v2.png" />
            
            </figure><p>With volatile access compiler doesn't promote the subtraction to 64 bits! Don't ask me why, although I would love to hear an explanation. For now, consider this a hack. One that does not come for free - there is the overhead of going through the stack on each read/write.</p><p>However, if we play our cards right we just might reduce it a little. We don't actually need the volatile load or store to happen, we just want the side effect. So instead of declaring the value as volatile, which implies that both reads and writes are volatile, let's try to make only the writes volatile with a help of a macro:</p>
            <pre><code>/* Emits a "store volatile" in LLVM IR */
#define ST_V(rhs, lhs) (*(volatile typeof(rhs) *) &amp;(rhs) = (lhs))</code></pre>
            <p>If this macro looks strangely familiar, it's because it does the same thing as <a href="https://elixir.bootlin.com/linux/v5.1-rc5/source/include/linux/compiler.h#L214"><code>WRITE_ONCE()</code> macro</a> in the Linux kernel. Applying it to our example:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3qQ1CrUvUBsJJE09xVqzA1/bc84a1c3fdd5a855c505c56c2273e664/sub32_v3.png" />
            
            </figure><p>That's another <a href="https://github.com/cloudflare/cloudflare-blog/blob/master/2019-04-ebpf-alu32/bpf/filter.c#L143">hacky but working solution</a>. Pick your poison.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/28GXw3wm8YlUgg9nvnBwEx/2939bf7b36758a66685385e16a48cc5f/poison_bottles-2.jpg" />
            
            </figure><p><a href="https://creativecommons.org/licenses/by-sa/3.0/">CC BY-SA 3.0</a> <a href="https://commons.wikimedia.org/wiki/File:D-BW-Kressbronn_aB_-_Kl%C3%A4ranlage_067.jpg">image</a> by ANKAWÜ</p><p>So there you have it - from C, to IR, and back to C to hack around a bug in eBPF verifier and be able to subtract 64-bit integers again. Usually you won't have to dive into LLVM IR or assembly to make use of eBPF. But it does help to know a little about it when things don't work as expected.</p><p>Did I mention that 64-bit addition is also broken? Have fun fixing it!</p><hr /><p><sup>1</sup> Okay, it was more like 3 months time until the bug was discovered and fixed.</p><p><sup>2</sup> Some even think that it is <a href="https://idea.popcount.org/2013-07-24-ir-is-better-than-assembly/">better than assembly</a>.</p><p><sup>3</sup> How do we know? The litmus test is to look for statements matching <code>r[0-9] [-+]= r[0-9]</code> in BPF assembly.</p> ]]></content:encoded>
            <category><![CDATA[eBPF]]></category>
            <category><![CDATA[Linux]]></category>
            <category><![CDATA[Programming]]></category>
            <guid isPermaLink="false">2fXVvP0CJO4Bt4KuJthpbx</guid>
            <dc:creator>Jakub Sitnicki</dc:creator>
        </item>
        <item>
            <title><![CDATA[Introducing ebpf_exporter]]></title>
            <link>https://blog.cloudflare.com/introducing-ebpf_exporter/</link>
            <pubDate>Fri, 24 Aug 2018 15:11:53 GMT</pubDate>
            <description><![CDATA[ Here at Cloudflare we use Prometheus to collect operational metrics. We run it on hundreds of servers and ingest millions of metrics per second to get insight into our network and provide the best possible service to our customers. ]]></description>
            <content:encoded><![CDATA[ <p><i>This is an adapted transcript of a talk I gave at Promcon 2018. You can find slides with additional information on our Prometheus deployment and presenter notes </i><a href="https://docs.google.com/presentation/d/1420m05QANTxrCPvwsLZSHWRTvqOsxCVRzJsVp9awoFg/edit?usp=sharing"><i>here</i></a><i>. There's also a </i><a href="https://www.youtube.com/watch?v=VvJx0WTiGcA&amp;feature=youtu.be&amp;t=1h15m40s"><i>video</i></a><i>.</i></p><p><i>Tip: you can click on the image to see the original large version.</i></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/18mND4203diqo8PdjA7mRW/3da65a7279fc7f0d8a172a7b59f9d7c1/1-1.jpeg.jpeg" />
            
            </figure><p>Here at Cloudflare we use <a href="https://prometheus.io/">Prometheus</a> to collect operational metrics. We run it on hundreds of servers and ingest millions of metrics per second to get insight into our network and provide the best possible service to our customers.</p><p>Prometheus metric format is popular enough, it's now being standardized as <a href="https://openmetrics.io/">OpenMetrics</a> under Cloud Native Computing Foundation. It's exciting to see convergence in long fragmented metrics landscape.</p><p>In this blog post we'll talk about how we measure low level metrics and share a tool that can help you to get similar understanding of your systems.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3T3BNATGvX6eNAUsUKkIPZ/284180e45e5a01a38ab1f0ba3724180d/2.jpg" />
            
            </figure><p>There are two main exporters one can use to get some insight into a Linux system performance.</p><p>The first one is <a href="https://github.com/prometheus/node_exporter">node_exporter</a> that gives you information about basics like CPU usage breakdown by type, memory usage, disk IO stats, filesystem and network usage.</p><p>The second one is <a href="https://github.com/google/cadvisor">cAdvisor</a>, that gives similar metrics, but drills down to a container level. Instead of seeing total CPU usage you can see which containers (and systemd units are also containers for <code>cAdvisor</code>) use how much of global resources.</p><p>This is the absolute minimum of what you should know about your systems. If you don’t have these two, you should start there.</p><p>Let’s look at the graphs you can get out of this.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3mGAxXcMQepPR40GK4F89O/a0eed601cd7899c5cd6ca16691676d30/3.jpg" />
            
            </figure><p>I should mention that every screenshot in this post is from a real production machine doing something useful. We have different generations of hardware, so don’t try to draw any conclusions.</p><p>Here you can see the basics you get from <code>node_exporter</code> for CPU and memory. You can see the utilization and how much slack resources you have.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4MqpWuek8lQlFfLsT6pft9/f13a1444f393e1d3836539c3bbe49f05/4.jpg" />
            
            </figure><p>Some more metrics from <code>node_exporter</code>, this time for disk IO. There are similar panels for network as well.</p><p>At the basic level you can do some correlation to explain why CPU went up if you see higher network and disk activity.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7eZyn1IguxbhJje2vUCnnr/5b6b77671ffdeb8baba4ee9b196e32ca/5.jpg" />
            
            </figure><p>With <code>cAdvisor</code> this gets more interesting, since you can now see how global resources like CPU are sliced between services. If CPU or memory usage grows, you can pinpoint exact service that is responsible and you can also see how it affects other services.</p><p>If global CPU numbers do not change much, you can still see shifts between services.</p><p>All of this information comes from the simplest counters and first derivatives (rates) on them.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6MQ3kdmLlcoNM7cmHewIAK/ce9f27bc965f08b46409d84a8b6e1eb8/6.jpg" />
            
            </figure><p>Counters are great, but they lack detail about individual events. Let’s take disk io for example. We get device io time from <code>node_exporter</code> and the derivative of that cannot go beyond one second per one second of real time, which means we can draw a bold red line at 1s and see what kind of utilization we get from our disks.</p><p>We get one number that characterizes out workload, but that’s simply not enough to understand it. Are we doing many fast IO operations? Are we doing few slow ones? What kind of mix of slow vs fast do we get? How are writes and reads different?</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3YIVj2gffdTs2JG1SSFX3R/719fd6f86f9b25305f19769add917873/7.jpg" />
            
            </figure><p>These questions beg for a histogram. What we have is a counter above, but what we want is a histogram below.</p><p>Keep in mind that Prometheus histograms are cumulative and <code>le</code> label counts all events lower than or equal to the value of the label.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1t0yc5ybRD1PQG0tjchlIA/73b00b5fc13ab7892c32fcc4ad0e605d/8.jpg" />
            
            </figure><p>Okay, so imagine we have that histogram. To visualize the difference you get between the two types of metrics here are two screenshots of the same event, which is an SSD replacement in production. We replaced a disk and this is how it affected the line and the histogram. In the new Grafana 5 you can plot histograms as heatmaps and it gives you a lot more detail than just one line.</p><p>The next slide has a bigger screenshot, on this slide you should just see the shift in detail.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3Zz5RAoWPkT1BrcHHuW01k/447effb0dbd21c298b3c590997778a84/9.jpg" />
            
            </figure><p>Here you can see buckets color coded and each timeslot has its own distribution in a tooltip. It can definitely get easier to understand with bar highlights and double Y axis, but it’s a big step forward from just one line nonetheless.</p><p>In addition to nice visualizations, you can also plot number of events above Xms and have alerts and SLOs on that. For example, you can alert if your read latency for block devices exceeds 10ms.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/45nmrTPwGnckajjF5H0aul/3fb2dee2217f4c4b10a79bc6723db492/10.jpg" />
            
            </figure><p>And if you were looking closely at these histograms, you may have noticed values on the Y axis are kind of high. Before the replacement you can see values in 0.5s to 1.0s bucket.</p><p>Tech specs for the left disk give you 50 microsecond read/write latency and on the right you get a slight decrease to 36 microseconds. That’s not what we see on the histogram at all. Sometimes you can spot this with fio in testing, but production workloads may have patterns that are difficult to replicate and have very different characteristics. Histograms show how it is.</p><p>Even a few slow requests can hurt overall numbers if you're not careful with IO. We've blogged <a href="/how-we-scaled-nginx-and-saved-the-world-54-years-every-day/">how this affected our cache latency and how we worked around this</a> recently.</p><p>By now you should be convinced that histograms are clearly superior for events where you care about timings of individual events.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/18YOVcmR0ijgFssnU0OkjL/5db42ab73a2fbced8042aa3fb5ab4caa/11.jpg" />
            
            </figure><p>And if you wanted to switch all your storage latency measurements to histograms, I have tough news for you: Linux kernel only provides counters for <code>node_exporter</code>.</p><p>You can try to mess with <code>blktrace</code> for this, but it doesn’t seem practical or efficient.</p><p>Let’s switch gears a little and see how summary stats from counters can be deceiving.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3CUg2pMqu8tqkMyAKZ7gcz/faad533c3b378a1b83b23fae6dc48065/12.gif" />
            
            </figure><p>This is a <a href="https://www.autodeskresearch.com/publications/samestats">research from Autodesk</a>. The creature on the left is called Datasaurus. Then there are target figures in the middle and animations on the right.</p><p>The amazing part is that shapes on the right and all intermediate animation frames for them have the same values for mean and standard deviation for both X and Y axis.</p><p>From summary statistic’s point of view every box on the right is same, but if you plot individual events, a very different picture emerges.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6CH5ajyCIJd50LBZcm3rbi/b66fcdace86472d4e19dd7a182c6f82b/13.gif" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/ZHKVCNSAcWE98AHBHyWiq/2f510c3ac23236bcb170576feafa88a2/14.gif" />
            
            </figure><p>These animations are from the same research, here you can clearly see how box plots (mean + stddev) are non-representative of the raw events.</p><p>Histograms, on the contrary, give an accurate picture.</p><p>We established that histograms are what you want, but you need individual events to make those histograms. What are the requirements for a system that would handle this task, assuming that we want to measure things like io operations in the Linux kernel?</p><ul><li><p>It has to be low overhead, otherwise we can’t run it in production</p></li><li><p>It has to be universal, so we are not locked into just io tracing</p></li><li><p>It has to be supported out of the box, third party kernel modules and patches are not very practical</p></li><li><p>And finally it has to be safe, we don’t want to crash large chunk of the internet we're responsible for to get some metrics, even if they are interesting</p></li></ul>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7LXeOhEUMR6KeP1lBkP0vy/1ef1af93f93773f486b1fca187f05165/16.jpg" />
            
            </figure><p>And it turns out, there’s a solution called eBPF. It’s a low overhead sandboxed user-defined bytecode running in the kernel. It can never crash, hang or interfere with the kernel negatively. That sounds kind of vague, but here are <a href="http://www.brendangregg.com/ebpf.html">two</a> <a href="https://cilium.readthedocs.io/en/v1.1/bpf/">links</a> that dive into the details explaining how this works.</p><p>The main part is that it’s already included with the Linux kernel. It’s used in networking subsystem and things like seccomp rules, but as a general “run safe code in the kernel” it has many uses beyond that.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/IJuUL6fQtULz0BDkn8G9q/002a9dd408b3709441004a34682fa3ba/17.jpg" />
            
            </figure><p>We said it’s a bytecode and this is how it looks. The good part is that you never have to write this by hand.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6fsvOUVCsMd6akmeoJXxQM/645d06bfd40b00a1e36f63f3f47e035d/18.jpg" />
            
            </figure><p>To use eBPF you write small C programs that attach to kernel functions and run before or after them. Your C code is then compiled into bytecode, verified and loaded into kernel to run with JIT compiler for translation into native opcodes. The constraints on the code are enforced by verifier and you are guaranteed to not be able to write an infinite loop or allocate tons of memory.</p><p>To share data with eBPF programs, you can use maps. In terms of metric collection, maps are updated by eBPF programs in the kernel and only accessed by userspace during scraping.</p><p>It is critical for performance that the eBPF VM runs in the kernel and does not cross userspace boundary.</p><p>You can see the workflow on the image above.</p><p>Having to write C is not an eBPF constraint, you can produce bytecode in any way you want. There are other alternatives like lua and ply, and sysdig is adding a backed to run via eBPF too. Maybe someday people will be writing <i>safe javacript</i> that runs in the kernel.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2sHoct70uBGUaPjVewdhbe/6e6caadf654056ff64fb32f4d4d69248/19.jpg" />
            
            </figure><p>Just like GCC compiles C code into machine code, BCC compiles C code into eBPF opcodes. BCC is a rewriter plus LLVM compiler and you can use it as a library in your code. There are bindings for C, C++, Python and Go.</p><p>In this example we have a simple function that runs after <code>d_lookup</code> kernel function that is responsible for directory cache lookups. It doesn’t look complicated and the basics should be understandable for people familiar with C-like languages.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1HWxAcgNhOgNZOHMHpfUnE/2bbf6966e5cd15a5d75a22a187cd535d/20.jpg" />
            
            </figure><p>In addition to a compiler, BCC includes some tools that can be used for debugging production systems with low overhead eBPF provides.</p><p>Let’s quickly go over a few of them.</p><p>The one above is <code>biolatency</code>, which shows you a histogram of disk io operations. That’s exactly what we started with and it’s already available, just as a script instead of an exporter for Prometheus.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2zNgKf8E4AYTGH037ukLtv/5e519a923f5b6dbb3d011cd8f3ed4420/21.jpg" />
            
            </figure><p>Here’s <code>execsnoop</code>, that allows you to see which commands are being launched in the system. This is often useful if you want to catch quickly terminating programs that do not hang around long enough to be observed in ps output.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2YYpWFjqFQ7VfdIz7QnD63/f4a8a5955b95ea121a2b644f57bcee1e/22.jpg" />
            
            </figure><p>There’s also <code>ext4slower</code> that instead of showing slow IO operations, shows slow ext4 filesystem operations. One might think these two map fairly closely, but one filesystem operation does not necessarily map to one disk IO operation:</p><ul><li><p>Writes can go into writeback cache and not touch the disk until later</p></li><li><p>Reads can involve multiple IOs to the physical device</p></li><li><p>Reads can also be blocked behind async writes</p></li></ul><p>The more you know about disk io, the more you want to run stateless, really. Sadly, RAM prices are not going down.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1aeArzz4a7mCh4YCzdFyLe/f7fbc1fcd278cd33367a3b69287df44d/23.jpg" />
            
            </figure><p>Okay, now to the main idea of this post. We have all these primitives, now we should be able to tie them all together and get an <a href="https://github.com/cloudflare/ebpf_exporter">ebpf_exporter</a> on our hands to get metrics in Prometheus where they belong. Many BCC tools already have kernel side ready, reviewed and battle tested, so the hardest part is covered.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/xt1nT5FC3EUK1vwQrd81d/6dcc656a8629685a5e38e8947c85f6e5/24.jpg" />
            
            </figure><p>Let’s look at a simple example to get counters for timers fired in the kernel. This example is from the <code>ebpf_exporter</code> repo, where you can find a few more complex ones.</p><p>On the BCC code side we define a hash and attach to a tracepoint. When the tracepoint fires, we increment our hash where the key is the address of a function that fired the tracepoint.</p><p>On the exporter side we say that we want to take the hash and transform 8 byte keys with <code>ksym</code> decoder. Kernel keeps a map of function addresses to their names in <code>/proc/kallsyms</code> and we just use that. We also define that we want to attach our function to <code>timer:timer_start</code> tracepoint.</p><p>This is objectively quite verbose and perhaps some things can be inferred instead of being explicit.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/26VdDuQstj0hI5fnTpGyJV/e76c62c263fa228444ff5f596d7c155d/25.jpg" />
            
            </figure><p>This is an example graph we can get out of this exporter config.</p><p>Why can this be useful? You may remember our <a href="/tracing-system-cpu-on-debian-stretch/">blog post about our tracing of a weird bug during OS upgrade from Debian Jessie to Stretch</a>.</p><p>TL;DR is that systemd bug broke TCP segmentation offload on vlan interface, which increased CPU load 5x and introduced lots of interesting side effects up to memory allocation stalls. You do not want to see that allocating one page takes 12 seconds, but that's exactly what we were seeing.</p><p>If we had timer metrics enabled, we would have seen the clear change in metrics sooner.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1etzKv3o5BpyvcStqvPthZ/b84a5cf27397dbb82bbb98f861b971e8/27.jpg" />
            
            </figure><p>Another bundled example can give you IPC or instruction per cycle metrics for each CPU. On this production system we can see a few interesting things:</p><ul><li><p>Not all cores are equal, with two cores being outliers (green on the top is zero, yellow on the bottom is one)</p></li><li><p>Something happened that dropped IPC of what looks like half of cores</p></li><li><p>There’s some variation in daily load cycles that affects IPC</p></li></ul>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6KvhwqZATxvQzEmG4gL92j/1d6870183040fb7e23b280d1875f6fac/28.jpg" />
            
            </figure><p>Why can this be useful? Check out <a href="http://www.brendangregg.com/blog/2017-05-09/cpu-utilization-is-wrong.html">Brendan Gregg’s somewhat controversial blog post about CPU utilization</a>.</p><p>TL;DR is that CPU% does not include memory stalls that do not perform any useful compute work. IPC helps to understand that factor better.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5Qr8BgK8JBbfpt0LaEjFHt/ecaaca96e411f8c32f0cd9bba3ec0dde/29.jpg" />
            
            </figure><p>Another bundled example is LLC or L3 CPU cache hit rate. This is from the same machine as IPC metrics and you can see some major affecting not just IPC, but also the hit rate.</p><p>Why can this be useful? You can see how your CPU cache is doing and you can see how it may be affected by bigger L3 cache or more cores sharing the same cache.</p><p>LLC hit rate usually goes hand in hand with IPC patterns as well.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/QoFIgsgz0EfXBDqdQdK5E/7f5b29176f3271b693e9bf95f9f4d84a/31.jpg" />
            
            </figure><p>Now to the fun part. We started with IO latency histograms and they are bundled with <code>ebpf_exporter</code> examples.</p><p>This is also a histogram, but now for run queue latency. When a process is woken up in the kernel, it’s ready to run. If CPU is idle, it can run immediately and scheduling delay is zero. If CPU is busy doing some other work, the process is put on a run queue and the CPU picks it up when it’s available. That delay between being ready to run and actually running is the scheduling delay and it affects latency for interactive applications.</p><p>In <code>cAdvisor</code> you have a counter with this delay, here you have a histogram of actual values of that delay.</p><p>Understanding of how you can be delayed is important for understanding the causes of externally visible latency. Check out <a href="http://www.brendangregg.com/blog/2017-03-16/perf-sched.html">another blog post by Brendan Gregg</a> to see how you can further trace and understand scheduling delays with Linux perf tool. It's quite surprising how high delay can be on even an even lighty loaded machine.</p><p>It also helps to have a metrics that you can observe if you change any scheduling sysctls in the kernel. The law is that you can never trust internet or even your own judgement to change any sysctls. If you can’t measure the effects, you are lying to yourself.</p><p>There’s also <a href="https://www.scylladb.com/2016/06/10/read-latency-and-scylla-jmx-process/">another great post from Scylla people</a> about their finds. We were able to apply their sysctls and observe results, which was quite satisfying.</p><p>Things like pinning processes to CPUs also affects scheduling, so this metric is invaluable for such experiments.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6HUpT97jGZskdDVvit0v9/618108b4c883b925232342200d99d88d/33.jpg" />
            
            </figure><p>The examples we gave are not the only ones possible. In addition to IPC and LLC metrics there are around 500 hardware metrics you can get on a typical server.</p><p>There are around 2000 tracepoints with stable ABI you can use on many kernel subsystems.</p><p>And you can always trace any non-inlined kernel function with kprobes and kretprobes, but nobody guarantees binary compatibility between releases for those. Some are stable, others not so much.</p><p>We don’t have support for user statically defined tracepoints or uprobes, which means you cannot trace userspace applications. This is something we can reconsider in the future, but in the meantime you can always add metrics to your apps by regular means.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5gux5cEXorn2uWkSHRVJNU/a85579d75fe935fb68b322d1d7984cf4/34.jpg" />
            
            </figure><p>This image shows tools that BCC provides, you can see it covers many aspects.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5BfGSsDTA2kyslIA1E6Nef/9b6092913c922a6427e86848a07c2491/35.jpg" />
            
            </figure><p>Nothing in this life is free and even low overhead still means some overhead. You should measure this in your own environment, but this is <a href="https://github.com/cloudflare/ebpf_exporter/tree/master/benchmark">what we’ve seen</a>.</p><p>For a fast <code>getpid()</code> syscall you get around 34% overhead if you count them by PID. 34% sounds like a lot, but it’s the simplest syscall and 100ns overhead is a cost of one memory reference.</p><p>For a complex case where we mix command name to copy some memory and mix in some randomness, the number jumps to 330ns or 105% overhead. We can still do 1.55M ops/s instead of 3.16M ops/s per logical core. If you measure something that doesn’t happen as often on each core, you’re probably not going to notice it as much.</p><p>We've seen run queue latency histogram to add 300ms of system time on a machine with 40 logical cores and 250K scheduling events per second.</p><p>Your mileage may vary.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2hrJTvorfU26Ee1sLmPByS/0b468fc0f031055d0871fae1f13e7e68/36.jpg" />
            
            </figure><p>So where should you run the exporter then? The answer is anywhere you feel comfortable.</p><p>If you run simple programs, it doesn’t hurt to run anywhere. If you are concerned about overhead, you can do it only on canary instances and hope for results to be representative. We do exactly that, but instead of canary instances we have a luxury of having a couple of canary datacenters with live customers. They get updates after our offices do, so it’s not as bad as it sounds.</p><p>For some upgrade events you may want to enable more extensive metrics. An example would be a major distro or kernel upgrade.</p><p>The last thing we wanted to mention is that <code>ebpf_exporter</code> is open source and we encourage you to try it and maybe contribute interesting examples that may be useful to others.</p><p>GitHub: <a href="https://github.com/cloudflare/ebpf_exporter">https://github.com/cloudflare/ebpf_exporter</a></p><p>Reading materials on eBPF:</p><ul><li><p><a href="https://iovisor.github.io/bcc/">https://iovisor.github.io/bcc/</a></p></li><li><p><a href="https://github.com/iovisor/bcc/blob/master/docs/reference_guide.md">https://github.com/iovisor/bcc/blob/master/docs/reference_guide.md</a></p></li><li><p><a href="http://www.brendangregg.com/ebpf.html">http://www.brendangregg.com/ebpf.html</a></p></li><li><p><a href="http://docs.cilium.io/en/latest/bpf/">http://docs.cilium.io/en/latest/bpf/</a></p></li></ul><p><i>While this post was in drafts, we added another example for </i><a href="https://github.com/cloudflare/ebpf_exporter/pull/35"><i>tracing port exhaustion issues</i></a><i> and that took under 10 minutes to write.</i></p> ]]></content:encoded>
            <category><![CDATA[Speed & Reliability]]></category>
            <category><![CDATA[eBPF]]></category>
            <category><![CDATA[Linux]]></category>
            <category><![CDATA[Programming]]></category>
            <guid isPermaLink="false">4qKVSXkNajj8QJP6vnQdnc</guid>
            <dc:creator>Ivan Babrou</dc:creator>
        </item>
        <item>
            <title><![CDATA[Tracing System CPU on Debian Stretch]]></title>
            <link>https://blog.cloudflare.com/tracing-system-cpu-on-debian-stretch/</link>
            <pubDate>Sun, 13 May 2018 16:00:00 GMT</pubDate>
            <description><![CDATA[ How an innocent OS upgrade triggered a cascade of issues and forced us into tracing Linux networking internals. ]]></description>
            <content:encoded><![CDATA[ <p><i>This is a heavily truncated version of an internal blog post from August 2017. For more recent updates on Kafka, check out </i><a href="/squeezing-the-firehose/"><i>another blog post on compression</i></a><i>, where we optimized throughput 4.5x for both disks and network.</i></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6o0r4Jk1oqG6ncMv8xWNb3/08bd0256a7509447b87aa08a7e7305f5/photo-1511971523672-53e6411f62b9" />
            
            </figure><p>Photo by <a href="https://unsplash.com/@alex_povolyashko?utm_source=ghost&amp;utm_medium=referral&amp;utm_campaign=api-credit">Alex Povolyashko</a> / <a href="https://unsplash.com/?utm_source=ghost&amp;utm_medium=referral&amp;utm_campaign=api-credit">Unsplash</a></p>
    <div>
      <h3>Upgrading our systems to Debian Stretch</h3>
      <a href="#upgrading-our-systems-to-debian-stretch">
        
      </a>
    </div>
    <p>For quite some time we've been rolling out Debian Stretch, to the point where we have reached ~10% adoption in our core datacenters. As part of upgarding the underlying OS, we also evaluate the higher level software stack, e.g. taking a look at our ClickHouse and Kafka clusters.</p><p>During our upgrade of Kafka, we sucessfully migrated two smaller clusters, <code>logs</code> and <code>dns</code>, but ran into issues when attempting to upgrade one of our larger clusters, <code>http</code>.</p><p>Thankfully, we were able to roll back the <code>http</code> cluster upgrade relatively easily, due to heavy versioning of both the OS and the higher level software stack. If there's one takeaway from this blog post, it's to take advantage of consistent versioning.</p>
    <div>
      <h3>High level differences</h3>
      <a href="#high-level-differences">
        
      </a>
    </div>
    <p>We upgraded one Kafka <code>http</code> node, and it did not go as planned:</p>
            <figure>
            <a href="http://staging.blog.mrk.cfdata.org/content/images/2018/04/1.png">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2hbPsT1ahBYgS806ztIpG3/070d402c9a5c1d38f257d65d87252f6c/1.png" />
            </a>
            </figure><p>Having 5x CPU usage was definitely an unexpected outcome. For control datapoints, we compared to a node where no upgrade happened, and an intermediary node that received a software stack upgrade, but not an OS upgrade. Neither of these two nodes experienced the same CPU saturation issues, even though their setups were practically identical.</p><p>For debugging CPU saturation issues, we call on <code>perf</code> to fish out details:</p>
            <figure>
            <a href="http://staging.blog.mrk.cfdata.org/content/images/2018/04/2-3.png">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2FsdAiwWMDh5t6VTTLuSV9/14c09b1b8fc3053a3dd4d49ff467f19a/2-3.png" />
            </a>
            </figure><p><i>The command used was: </i><code><i>perf top -F 99</i></code><i>.</i></p>
    <div>
      <h3>RCU stalls</h3>
      <a href="#rcu-stalls">
        
      </a>
    </div>
    <p>In addition to higher system CPU usage, we found secondary slowdowns, including <a href="http://www.rdrop.com/~paulmck/RCU/whatisRCU.html">read-copy update (RCU)</a> stalls:</p>
            <pre><code>[ 4909.110009] logfwdr (26887) used greatest stack depth: 11544 bytes left
[ 4909.392659] oom_reaper: reaped process 26861 (logfwdr), now anon-rss:8kB, file-rss:0kB, shmem-rss:0kB
[ 4923.462841] INFO: rcu_sched self-detected stall on CPU
[ 4923.462843]  13-...: (2 GPs behind) idle=ea7/140000000000001/0 softirq=1/2 fqs=4198
[ 4923.462845]   (t=8403 jiffies g=110722 c=110721 q=6440)</code></pre>
            <p>We've seen RCU stalls before, and our (suboptimal) solution was to reboot the machine.</p><p>However, one can only handle so many reboots before the problem becomes severe enough to warrant a deep dive. During our deep dive, we noticed in <code>dmesg</code> that we had issues allocating memory, while trying to write errors:</p>
            <pre><code>Aug 15 21:51:35 myhost kernel: INFO: rcu_sched detected stalls on CPUs/tasks:
Aug 15 21:51:35 myhost kernel:         26-...: (1881 ticks this GP) idle=76f/140000000000000/0 softirq=8/8 fqs=365
Aug 15 21:51:35 myhost kernel:         (detected by 0, t=2102 jiffies, g=1837293, c=1837292, q=262)
Aug 15 21:51:35 myhost kernel: Task dump for CPU 26:
Aug 15 21:51:35 myhost kernel: java            R  running task    13488  1714   1513 0x00080188
Aug 15 21:51:35 myhost kernel:  ffffc9000d1f7898 ffffffff814ee977 ffff88103f410400 000000000000000a
Aug 15 21:51:35 myhost kernel:  0000000000000041 ffffffff82203142 ffffc9000d1f78c0 ffffffff814eea10
Aug 15 21:51:35 myhost kernel:  0000000000000041 ffffffff82203142 ffff88103f410400 ffffc9000d1f7920
Aug 15 21:51:35 myhost kernel: Call Trace:
Aug 15 21:51:35 myhost kernel:  [&lt;ffffffff814ee977&gt;] ? scrup+0x147/0x160
Aug 15 21:51:35 myhost kernel:  [&lt;ffffffff814eea10&gt;] ? lf+0x80/0x90
Aug 15 21:51:35 myhost kernel:  [&lt;ffffffff814eecb5&gt;] ? vt_console_print+0x295/0x3c0
Aug 15 21:51:35 myhost kernel:  [&lt;ffffffff810b1193&gt;] ? call_console_drivers.isra.22.constprop.30+0xf3/0x100
Aug 15 21:51:35 myhost kernel:  [&lt;ffffffff810b1f51&gt;] ? console_unlock+0x281/0x550
Aug 15 21:51:35 myhost kernel:  [&lt;ffffffff810b2498&gt;] ? vprintk_emit+0x278/0x430
Aug 15 21:51:35 myhost kernel:  [&lt;ffffffff810b27ef&gt;] ? vprintk_default+0x1f/0x30
Aug 15 21:51:35 myhost kernel:  [&lt;ffffffff811588df&gt;] ? printk+0x48/0x50
Aug 15 21:51:35 myhost kernel:  [&lt;ffffffff810b30ee&gt;] ? dump_stack_print_info+0x7e/0xc0
Aug 15 21:51:35 myhost kernel:  [&lt;ffffffff8142d41f&gt;] ? dump_stack+0x44/0x65
Aug 15 21:51:35 myhost kernel:  [&lt;ffffffff81162e64&gt;] ? warn_alloc+0x124/0x150
Aug 15 21:51:35 myhost kernel:  [&lt;ffffffff81163842&gt;] ? __alloc_pages_slowpath+0x932/0xb80
Aug 15 21:51:35 myhost kernel:  [&lt;ffffffff81163c92&gt;] ? __alloc_pages_nodemask+0x202/0x250
Aug 15 21:51:35 myhost kernel:  [&lt;ffffffff811ae9c2&gt;] ? alloc_pages_current+0x92/0x120
Aug 15 21:51:35 myhost kernel:  [&lt;ffffffff81159d2f&gt;] ? __page_cache_alloc+0xbf/0xd0
Aug 15 21:51:35 myhost kernel:  [&lt;ffffffff8115cdfa&gt;] ? filemap_fault+0x2ea/0x4d0
Aug 15 21:51:35 myhost kernel:  [&lt;ffffffff8136dc95&gt;] ? xfs_filemap_fault+0x45/0xa0
Aug 15 21:51:35 myhost kernel:  [&lt;ffffffff8118b3eb&gt;] ? __do_fault+0x6b/0xd0
Aug 15 21:51:35 myhost kernel:  [&lt;ffffffff81190028&gt;] ? handle_mm_fault+0xe98/0x12b0
Aug 15 21:51:35 myhost kernel:  [&lt;ffffffff8110756b&gt;] ? __seccomp_filter+0x1db/0x290
Aug 15 21:51:35 myhost kernel:  [&lt;ffffffff8104fa5c&gt;] ? __do_page_fault+0x22c/0x4c0
Aug 15 21:51:35 myhost kernel:  [&lt;ffffffff8104fd10&gt;] ? do_page_fault+0x20/0x70
Aug 15 21:51:35 myhost kernel:  [&lt;ffffffff819bea02&gt;] ? page_fault+0x22/0x30</code></pre>
            <p>This suggested that we were logging too many errors, and the actual failure may be earlier in the process. Armed with this hypothesis, we looked at the very beginning of the error chain:</p>
            <pre><code>Aug 16 01:14:51 myhost systemd-journald[13812]: Missed 17171 kernel messages
Aug 16 01:14:51 myhost kernel:  [&lt;ffffffff81171754&gt;] shrink_inactive_list+0x1f4/0x4f0
Aug 16 01:14:51 myhost kernel:  [&lt;ffffffff8117234b&gt;] shrink_node_memcg+0x5bb/0x780
Aug 16 01:14:51 myhost kernel:  [&lt;ffffffff811725e2&gt;] shrink_node+0xd2/0x2f0
Aug 16 01:14:51 myhost kernel:  [&lt;ffffffff811728ef&gt;] do_try_to_free_pages+0xef/0x310
Aug 16 01:14:51 myhost kernel:  [&lt;ffffffff81172be5&gt;] try_to_free_pages+0xd5/0x180
Aug 16 01:14:51 myhost kernel:  [&lt;ffffffff811632db&gt;] __alloc_pages_slowpath+0x31b/0xb80</code></pre>
            <p>As much as <code>shrink_node</code> may scream "NUMA issues", you're looking primarily at:</p>
            <pre><code>Aug 16 01:14:51 myhost systemd-journald[13812]: Missed 17171 kernel messages</code></pre>
            <p>In addition, we also found memory allocation issues:</p>
            <pre><code>[78972.506644] Mem-Info:
[78972.506653] active_anon:3936889 inactive_anon:371971 isolated_anon:0
[78972.506653]  active_file:25778474 inactive_file:1214478 isolated_file:2208
[78972.506653]  unevictable:0 dirty:1760643 writeback:0 unstable:0
[78972.506653]  slab_reclaimable:1059804 slab_unreclaimable:141694
[78972.506653]  mapped:47285 shmem:535917 pagetables:10298 bounce:0
[78972.506653]  free:202928 free_pcp:3085 free_cma:0
[78972.506660] Node 0 active_anon:8333016kB inactive_anon:989808kB active_file:50622384kB inactive_file:2401416kB unevictable:0kB isolated(anon):0kB isolated(file):3072kB mapped:96624kB dirty:3422168kB writeback:0kB shmem:1261156kB shmem_thp: 0kB shmem_pmdmapped: 0kB anon_thp: 0kB writeback_tmp:0kB unstable:0kB pages_scanned:15744 all_unreclaimable? no
[78972.506666] Node 1 active_anon:7414540kB inactive_anon:498076kB active_file:52491512kB inactive_file:2456496kB unevictable:0kB isolated(anon):0kB isolated(file):5760kB mapped:92516kB dirty:3620404kB writeback:0kB shmem:882512kB shmem_thp: 0kB shmem_pmdmapped: 0kB anon_thp: 0kB writeback_tmp:0kB unstable:0kB pages_scanned:9080974 all_unreclaimable? no
[78972.506671] Node 0 DMA free:15900kB min:100kB low:124kB high:148kB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:0kB writepending:0kB present:15996kB managed:15900kB mlocked:0kB slab_reclaimable:0kB slab_unreclaimable:0kB kernel_stack:0kB pagetables:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB
** 9 printk messages dropped ** [78972.506716] Node 0 Normal: 15336*4kB (UMEH) 4584*8kB (MEH) 2119*16kB (UME) 775*32kB (MEH) 106*64kB (UM) 81*128kB (MH) 29*256kB (UM) 25*512kB (M) 19*1024kB (M) 7*2048kB (M) 2*4096kB (M) = 236080kB
[78972.506725] Node 1 Normal: 31740*4kB (UMEH) 3879*8kB (UMEH) 873*16kB (UME) 353*32kB (UM) 286*64kB (UMH) 62*128kB (UMH) 28*256kB (MH) 20*512kB (UMH) 15*1024kB (UM) 7*2048kB (UM) 12*4096kB (M) = 305752kB
[78972.506726] Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=2048kB
[78972.506727] Node 1 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=2048kB
[78972.506728] 27531091 total pagecache pages
[78972.506729] 0 pages in swap cache
[78972.506730] Swap cache stats: add 0, delete 0, find 0/0
[78972.506730] Free swap  = 0kB
[78972.506731] Total swap = 0kB
[78972.506731] 33524975 pages RAM
[78972.506732] 0 pages HighMem/MovableOnly
[78972.506732] 546255 pages reserved
[78972.620129] ntpd: page allocation stalls for 272380ms, order:0, mode:0x24000c0(GFP_KERNEL)
[78972.620132] CPU: 16 PID: 13099 Comm: ntpd Tainted: G           O    4.9.43-cloudflare-2017.8.4 #1
[78972.620133] Hardware name: Quanta Computer Inc D51B-2U (dual 1G LoM)/S2B-MB (dual 1G LoM), BIOS S2B_3A21 10/01/2015
[78972.620136]  ffffc90022f9b6f8 ffffffff8142d668 ffffffff81ca31b8 0000000000000001
[78972.620138]  ffffc90022f9b778 ffffffff81162f14 024000c022f9b740 ffffffff81ca31b8
[78972.620140]  ffffc90022f9b720 0000000000000010 ffffc90022f9b788 ffffc90022f9b738
[78972.620140] Call Trace:
[78972.620148]  [&lt;ffffffff8142d668&gt;] dump_stack+0x4d/0x65
[78972.620152]  [&lt;ffffffff81162f14&gt;] warn_alloc+0x124/0x150
[78972.620154]  [&lt;ffffffff811638f2&gt;] __alloc_pages_slowpath+0x932/0xb80
[78972.620157]  [&lt;ffffffff81163d42&gt;] __alloc_pages_nodemask+0x202/0x250
[78972.620160]  [&lt;ffffffff811aeae2&gt;] alloc_pages_current+0x92/0x120
[78972.620162]  [&lt;ffffffff8115f6ee&gt;] __get_free_pages+0xe/0x40
[78972.620165]  [&lt;ffffffff811e747a&gt;] __pollwait+0x9a/0xe0
[78972.620168]  [&lt;ffffffff817c9ec9&gt;] datagram_poll+0x29/0x100
[78972.620170]  [&lt;ffffffff817b9d48&gt;] sock_poll+0x48/0xa0
[78972.620172]  [&lt;ffffffff811e7c35&gt;] do_select+0x335/0x7b0</code></pre>
            <p>This specific error message did seem fun:</p>
            <pre><code>[78991.546088] systemd-network: page allocation stalls for 287000ms, order:0, mode:0x24200ca(GFP_HIGHUSER_MOVABLE)</code></pre>
            <p>You don't want your page allocations to stall for 5 minutes, especially when it's order zero allocation (smallest allocation of one 4 KiB page).</p><p>Comparing to our control nodes, the only two possible explanations were: a kernel upgrade, and the switch from Debian Jessie to Debian Stretch. We suspected the former, since CPU usage implies a kernel issue. However, just to be safe, we rolled both the kernel back to 4.4.55, and downgraded the affected nodes back to Debian Jessie. This was a reasonable compromise, since we needed to minimize downtime on production nodes.</p>
    <div>
      <h3>Digging a bit deeper</h3>
      <a href="#digging-a-bit-deeper">
        
      </a>
    </div>
    <p>Keeping servers running on older kernel and distribution is not a viable long term solution. Through bisection, we found the issue lay in the Jessie to Stretch upgrade, contrary to our initial hypothesis.</p><p>Now that we knew what the problem was, we proceeded to investigate why. With the help from existing automation around <code>perf</code> and Java, we generated the following flamegraphs:</p><ul><li><p>Jessie</p></li></ul>
            <figure>
            <a href="http://staging.blog.mrk.cfdata.org/content/images/2018/04/9.png">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3fhMCSmQj4IC8MLxPN2d1V/60a107967bdede0ba8c4465090fb6ec4/9.png" />
            </a>
            </figure><ul><li><p>Stretch</p></li></ul>
            <figure>
            <a href="http://staging.blog.mrk.cfdata.org/content/images/2018/04/10.png">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6b523AB48TNUF2jj6OYhxi/9cadde05d8cf89187f182b56c48b3c1b/10.png" />
            </a>
            </figure><p>At first it looked like Jessie was doing <code>writev</code> instead of <code>sendfile</code>, but the full flamegraphs revealed that Strech was executing <code>sendfile</code> a lot slower.</p><p>If you highlight <code>sendfile</code>:</p><ul><li><p>Jessie</p></li></ul>
            <figure>
            <a href="http://staging.blog.mrk.cfdata.org/content/images/2018/04/11.png">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6XXts4Hvy58nwa8FG3ZNfT/2e36ce3aa2b111059bcff6a21e3da712/11.png" />
            </a>
            </figure><ul><li><p>Stretch</p></li></ul>
            <figure>
            <a href="http://staging.blog.mrk.cfdata.org/content/images/2018/04/12.png">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/32BB3vFsnbl7ul6b6Aa5MP/10788cfa90962c2034e7be7fc6b76a1f/12.png" />
            </a>
            </figure><p>And zoomed in:</p><ul><li><p>Jessie</p></li></ul>
            <figure>
            <a href="http://staging.blog.mrk.cfdata.org/content/images/2018/04/13.png">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/75TU9Q58iCRcCKAt6eZxf3/4cdf2f2038bb4e7ef813ba7e21562121/13.png" />
            </a>
            </figure><ul><li><p>Stretch</p></li></ul>
            <figure>
            <a href="http://staging.blog.mrk.cfdata.org/content/images/2018/04/14.png">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/fgUsgL2hUrhJHg3ns5HeE/3b733ecc2ed4ebee2a751394a81804fb/14.png" />
            </a>
            </figure><p>These two look very different.</p><p>Some colleagues suggested that the differences in the graphs may be due to TCP offload being disabled, but upon checking our NIC settings, we found that the feature flags were identical.</p><p>We'll dive into the differences in the next section.</p>
    <div>
      <h3>And deeper</h3>
      <a href="#and-deeper">
        
      </a>
    </div>
    <p>To trace latency distributions of <code>sendfile</code> syscalls between Jessie and Stretch, we used <a href="https://github.com/iovisor/bcc/blob/master/tools/funclatency_example.txt"><code>funclatency</code></a> from <a href="https://iovisor.github.io/bcc/">bcc-tools</a>:</p><ul><li><p>Jessie</p></li></ul>
            <pre><code>$ sudo /usr/share/bcc/tools/funclatency -uTi 1 do_sendfile
Tracing 1 functions for "do_sendfile"... Hit Ctrl-C to end.
23:27:25
     usecs               : count     distribution
         0 -&gt; 1          : 9        |                                        |
         2 -&gt; 3          : 47       |****                                    |
         4 -&gt; 7          : 53       |*****                                   |
         8 -&gt; 15         : 379      |****************************************|
        16 -&gt; 31         : 329      |**********************************      |
        32 -&gt; 63         : 101      |**********                              |
        64 -&gt; 127        : 23       |**                                      |
       128 -&gt; 255        : 50       |*****                                   |
       256 -&gt; 511        : 7        |                                        |</code></pre>
            <ul><li><p>Stretch</p></li></ul>
            <pre><code>$ sudo /usr/share/bcc/tools/funclatency -uTi 1 do_sendfile
Tracing 1 functions for "do_sendfile"... Hit Ctrl-C to end.
23:27:28
     usecs               : count     distribution
         0 -&gt; 1          : 1        |                                        |
         2 -&gt; 3          : 20       |***                                     |
         4 -&gt; 7          : 46       |*******                                 |
         8 -&gt; 15         : 56       |********                                |
        16 -&gt; 31         : 65       |**********                              |
        32 -&gt; 63         : 75       |***********                             |
        64 -&gt; 127        : 75       |***********                             |
       128 -&gt; 255        : 258      |****************************************|
       256 -&gt; 511        : 144      |**********************                  |
       512 -&gt; 1023       : 24       |***                                     |
      1024 -&gt; 2047       : 27       |****                                    |
      2048 -&gt; 4095       : 28       |****                                    |
      4096 -&gt; 8191       : 35       |*****                                   |
      8192 -&gt; 16383      : 1        |                                        |</code></pre>
            <p>In the flamegraphs, you can see timers being set at the tip (<code>mod_timer</code> function), with these timers taking locks. On Stretch we installed 3x more timers, resulting in 10x the amount of contention:</p><ul><li><p>Jessie</p></li></ul>
            <pre><code>$ sudo /usr/share/bcc/tools/funccount -T -i 1 mod_timer
Tracing 1 functions for "mod_timer"... Hit Ctrl-C to end.
00:33:36
FUNC                                    COUNT
mod_timer                               60482
00:33:37
FUNC                                    COUNT
mod_timer                               58263
00:33:38
FUNC                                    COUNT
mod_timer                               54626</code></pre>
            
            <pre><code>$ sudo /usr/share/bcc/tools/funccount -T -i 1 lock_timer_base
Tracing 1 functions for "lock_timer_base"... Hit Ctrl-C to end.
00:32:36
FUNC                                    COUNT
lock_timer_base                         15962
00:32:37
FUNC                                    COUNT
lock_timer_base                         16261
00:32:38
FUNC                                    COUNT
lock_timer_base                         15806</code></pre>
            <ul><li><p>Stretch</p></li></ul>
            <pre><code>$ sudo /usr/share/bcc/tools/funccount -T -i 1 mod_timer
Tracing 1 functions for "mod_timer"... Hit Ctrl-C to end.
00:33:28
FUNC                                    COUNT
mod_timer                              149068
00:33:29
FUNC                                    COUNT
mod_timer                              155994
00:33:30
FUNC                                    COUNT
mod_timer                              160688</code></pre>
            
            <pre><code>$ sudo /usr/share/bcc/tools/funccount -T -i 1 lock_timer_base
Tracing 1 functions for "lock_timer_base"... Hit Ctrl-C to end.
00:32:32
FUNC                                    COUNT
lock_timer_base                        119189
00:32:33
FUNC                                    COUNT
lock_timer_base                        196895
00:32:34
FUNC                                    COUNT
lock_timer_base                        140085</code></pre>
            <p>The Linux kernel includes debugging facilities for timers, which <a href="https://elixir.bootlin.com/linux/v4.9.43/source/kernel/time/timer.c#L1010">call</a> the <code>timer:timer_start</code> <a href="https://elixir.bootlin.com/linux/v4.9.43/source/include/trace/events/timer.h#L44">tracepoint</a> on every timer start. This allowed us to pull up timer names:</p><ul><li><p>Jessie</p></li></ul>
            <pre><code>$ sudo perf record -e timer:timer_start -p 23485 -- sleep 10 &amp;&amp; sudo perf script | sed 's/.* function=//g' | awk '{ print $1 }' | sort | uniq -c
[ perf record: Woken up 54 times to write data ]
[ perf record: Captured and wrote 17.778 MB perf.data (173520 samples) ]
      6 blk_rq_timed_out_timer
      2 clocksource_watchdog
      5 commit_timeout
      5 cursor_timer_handler
      2 dev_watchdog
     10 garp_join_timer
      2 ixgbe_service_timer
     36 reqsk_timer_handler
   4769 tcp_delack_timer
    171 tcp_keepalive_timer
 168512 tcp_write_timer</code></pre>
            <ul><li><p>Stretch</p></li></ul>
            <pre><code>$ sudo perf record -e timer:timer_start -p 3416 -- sleep 10 &amp;&amp; sudo perf script | sed 's/.* function=//g' | awk '{ print $1 }' | sort | uniq -c
[ perf record: Woken up 671 times to write data ]
[ perf record: Captured and wrote 198.273 MB perf.data (1988650 samples) ]
      6 clocksource_watchdog
      4 commit_timeout
     12 cursor_timer_handler
      2 dev_watchdog
     18 garp_join_timer
      4 ixgbe_service_timer
      1 neigh_timer_handler
      1 reqsk_timer_handler
   4622 tcp_delack_timer
      1 tcp_keepalive_timer
1983978 tcp_write_timer
      1 writeout_period</code></pre>
            <p>So basically we install 12x more <code>tcp_write_timer</code> timers, resulting in higher kernel CPU usage.</p><p>Taking specific flamegraphs of the timers revealed the differences in their operation:</p><ul><li><p>Jessie</p></li></ul>
            <figure>
            <a href="http://staging.blog.mrk.cfdata.org/content/images/2018/04/15.png">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4PJjYK3FzgAeQxpbHPGn5i/06f546c8ea1cda3d58c4c54dd3618a15/15.png" />
            </a>
            </figure><ul><li><p>Stretch</p></li></ul>
            <figure>
            <a href="http://staging.blog.mrk.cfdata.org/content/images/2018/04/16.png">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7M8XyRvy7vHDytdpWJQXAr/784aa92acf4f92c8896d08e2fede9bcd/16.png" />
            </a>
            </figure><p>We then traced the functions that were different:</p><ul><li><p>Jessie</p></li></ul>
            <pre><code>$ sudo /usr/share/bcc/tools/funccount -T -i 1 tcp_sendmsg
Tracing 1 functions for "tcp_sendmsg"... Hit Ctrl-C to end.
03:33:33
FUNC                                    COUNT
tcp_sendmsg                             21166
03:33:34
FUNC                                    COUNT
tcp_sendmsg                             21768
03:33:35
FUNC                                    COUNT
tcp_sendmsg                             21712</code></pre>
            
            <pre><code>$ sudo /usr/share/bcc/tools/funccount -T -i 1 tcp_push_one
Tracing 1 functions for "tcp_push_one"... Hit Ctrl-C to end.
03:37:14
FUNC                                    COUNT
tcp_push_one                              496
03:37:15
FUNC                                    COUNT
tcp_push_one                              432
03:37:16
FUNC                                    COUNT
tcp_push_one                              495</code></pre>
            
            <pre><code>$ sudo /usr/share/bcc/tools/trace -p 23485 'tcp_sendmsg "%d", arg3' -T -M 100000 | awk '{ print $NF }' | sort | uniq -c | sort -n | tail
   1583 4
   2043 54
   3546 18
   4016 59
   4423 50
   5349 8
   6154 40
   6620 38
  17121 51
  39528 44</code></pre>
            <ul><li><p>Stretch</p></li></ul>
            <pre><code>$ sudo /usr/share/bcc/tools/funccount -T -i 1 tcp_sendmsg
Tracing 1 functions for "tcp_sendmsg"... Hit Ctrl-C to end.
03:33:30
FUNC                                    COUNT
tcp_sendmsg                             53834
03:33:31
FUNC                                    COUNT
tcp_sendmsg                             49472
03:33:32
FUNC                                    COUNT
tcp_sendmsg                             51221</code></pre>
            
            <pre><code>$ sudo /usr/share/bcc/tools/funccount -T -i 1 tcp_push_one
Tracing 1 functions for "tcp_push_one"... Hit Ctrl-C to end.
03:37:10
FUNC                                    COUNT
tcp_push_one                            64483
03:37:11
FUNC                                    COUNT
tcp_push_one                            65058
03:37:12
FUNC                                    COUNT
tcp_push_one                            72394</code></pre>
            
            <pre><code>$ sudo /usr/share/bcc/tools/trace -p 3416 'tcp_sendmsg "%d", arg3' -T -M 100000 | awk '{ print $NF }' | sort | uniq -c | sort -n | tail
    396 46
    409 4
   1124 50
   1305 18
   1547 40
   1672 59
   1729 8
   2181 38
  19052 44
  64504 4096</code></pre>
            <p>The traces showed huge variations of <code>tcp_sendmsg</code> and <code>tcp_push_one</code> within <code>sendfile</code>.</p><p>To further introspect, we leveraged a kernel feature available since 4.9: the ability to count stacks. This led us to measuring what hits <code>tcp_push_one</code>:</p><ul><li><p>Jessie</p></li></ul>
            <pre><code>$ sudo /usr/share/bcc/tools/stackcount -i 10 tcp_push_one
Tracing 1 functions for "tcp_push_one"... Hit Ctrl-C to end.
  tcp_push_one
  inet_sendmsg
  sock_sendmsg
  sock_write_iter
  do_iter_readv_writev
  do_readv_writev
  vfs_writev
  do_writev
  SyS_writev
  do_syscall_64
  return_from_SYSCALL_64
    1
  tcp_push_one
  inet_sendpage
  kernel_sendpage
  sock_sendpage
  pipe_to_sendpage
  __splice_from_pipe
  splice_from_pipe
  generic_splice_sendpage
  direct_splice_actor
  splice_direct_to_actor
  do_splice_direct
  do_sendfile
  sys_sendfile64
  do_syscall_64
  return_from_SYSCALL_64
    4950</code></pre>
            <ul><li><p>Stretch</p></li></ul>
            <pre><code>$ sudo /usr/share/bcc/tools/stackcount -i 10 tcp_push_one
Tracing 1 functions for "tcp_push_one"... Hit Ctrl-C to end.
  tcp_push_one
  inet_sendmsg
  sock_sendmsg
  sock_write_iter
  do_iter_readv_writev
  do_readv_writev
  vfs_writev
  do_writev
  SyS_writev
  do_syscall_64
  return_from_SYSCALL_64
    123
  tcp_push_one
  inet_sendmsg
  sock_sendmsg
  sock_write_iter
  __vfs_write
  vfs_write
  SyS_write
  do_syscall_64
  return_from_SYSCALL_64
    172
  tcp_push_one
  inet_sendmsg
  sock_sendmsg
  kernel_sendmsg
  sock_no_sendpage
  tcp_sendpage
  inet_sendpage
  kernel_sendpage
  sock_sendpage
  pipe_to_sendpage
  __splice_from_pipe
  splice_from_pipe
  generic_splice_sendpage
  direct_splice_actor
  splice_direct_to_actor
  do_splice_direct
  do_sendfile
  sys_sendfile64
  do_syscall_64
  return_from_SYSCALL_64
    735110</code></pre>
            <p>If you diff the most popular stacks, you'll get:</p>
            <pre><code>--- jessie.txt  2017-08-16 21:14:13.000000000 -0700
+++ stretch.txt 2017-08-16 21:14:20.000000000 -0700
@@ -1,4 +1,9 @@
 tcp_push_one
+inet_sendmsg
+sock_sendmsg
+kernel_sendmsg
+sock_no_sendpage
+tcp_sendpage
 inet_sendpage
 kernel_sendpage
 sock_sendpage</code></pre>
            <p>Let's look closer at <a href="https://elixir.bootlin.com/linux/v4.9.43/source/net/ipv4/tcp.c#L1012"><code>tcp_sendpage</code></a>:</p>
            <pre><code>int tcp_sendpage(struct sock *sk, struct page *page, int offset,
         size_t size, int flags)
{
    ssize_t res;

    if (!(sk-&gt;sk_route_caps &amp; NETIF_F_SG) ||
        !sk_check_csum_caps(sk))
        return sock_no_sendpage(sk-&gt;sk_socket, page, offset, size,
                    flags);

    lock_sock(sk);

    tcp_rate_check_app_limited(sk);  /* is sending application-limited? */

    res = do_tcp_sendpages(sk, page, offset, size, flags);
    release_sock(sk);
    return res;
}</code></pre>
            <p>It looks like we don't enter the <code>if</code> body. We looked up what <a href="https://elixir.bootlin.com/linux/v4.9.43/source/include/linux/netdev_features.h#L115">NET_F_SG</a> does: <a href="https://en.wikipedia.org/wiki/Large_send_offload">segmentation offload</a>. This difference is peculiar, since both OS'es should have this enabled.</p>
    <div>
      <h3>Even deeper, to the crux</h3>
      <a href="#even-deeper-to-the-crux">
        
      </a>
    </div>
    <p>It turned out that we had segmentation offload enabled for only a few of our NICs: <code>eth2</code>, <code>eth3</code>, and <code>bond0</code>. Our network setup can be described as follows:</p>
            <pre><code>eth2 --&gt;|              |--&gt; vlan10
        |---&gt; bond0 --&gt;|
eth3 --&gt;|              |--&gt; vlan100</code></pre>
            <p><b>The missing piece was that we were missing segmentation offload on VLAN interfaces, where the actual IPs live.</b></p><p>Here's the diff from <code>ethtook -k vlan10</code>:</p>
            <pre><code>$ diff -rup &lt;(ssh jessie sudo ethtool -k vlan10) &lt;(ssh stretch sudo ethtool -k vlan10)
--- /dev/fd/63  2017-08-16 21:21:12.000000000 -0700
+++ /dev/fd/62  2017-08-16 21:21:12.000000000 -0700
@@ -1,21 +1,21 @@
 Features for vlan10:
 rx-checksumming: off [fixed]
-tx-checksumming: off
+tx-checksumming: on
        tx-checksum-ipv4: off [fixed]
-       tx-checksum-ip-generic: off
+       tx-checksum-ip-generic: on
        tx-checksum-ipv6: off [fixed]
        tx-checksum-fcoe-crc: off
        tx-checksum-sctp: off
-scatter-gather: off
-       tx-scatter-gather: off
+scatter-gather: on
+       tx-scatter-gather: on
        tx-scatter-gather-fraglist: off
-tcp-segmentation-offload: off
-       tx-tcp-segmentation: off [requested on]
-       tx-tcp-ecn-segmentation: off [requested on]
-       tx-tcp-mangleid-segmentation: off [requested on]
-       tx-tcp6-segmentation: off [requested on]
-udp-fragmentation-offload: off [requested on]
-generic-segmentation-offload: off [requested on]
+tcp-segmentation-offload: on
+       tx-tcp-segmentation: on
+       tx-tcp-ecn-segmentation: on
+       tx-tcp-mangleid-segmentation: on
+       tx-tcp6-segmentation: on
+udp-fragmentation-offload: on
+generic-segmentation-offload: on
 generic-receive-offload: on
 large-receive-offload: off [fixed]
 rx-vlan-offload: off [fixed]</code></pre>
            <p>So we entusiastically enabled segmentation offload:</p>
            <pre><code>$ sudo ethtool -K vlan10 sg on</code></pre>
            <p>And it didn't help! Will the suffering ever end? Let's also enable TCP transmission checksumming offload:</p>
            <pre><code>$ sudo ethtool -K vlan10 tx on
Actual changes:
tx-checksumming: on
        tx-checksum-ip-generic: on
tcp-segmentation-offload: on
        tx-tcp-segmentation: on
        tx-tcp-ecn-segmentation: on
        tx-tcp-mangleid-segmentation: on
        tx-tcp6-segmentation: on
udp-fragmentation-offload: on</code></pre>
            <p>Nothing. The diff is essentially empty now:</p>
            <pre><code>$ diff -rup &lt;(ssh jessie sudo ethtool -k vlan10) &lt;(ssh stretch sudo ethtool -k vlan10)
--- /dev/fd/63  2017-08-16 21:31:27.000000000 -0700
+++ /dev/fd/62  2017-08-16 21:31:27.000000000 -0700
@@ -4,11 +4,11 @@ tx-checksumming: on
        tx-checksum-ipv4: off [fixed]
        tx-checksum-ip-generic: on
        tx-checksum-ipv6: off [fixed]
-       tx-checksum-fcoe-crc: off [requested on]
-       tx-checksum-sctp: off [requested on]
+       tx-checksum-fcoe-crc: off
+       tx-checksum-sctp: off
 scatter-gather: on
        tx-scatter-gather: on
-       tx-scatter-gather-fraglist: off [requested on]
+       tx-scatter-gather-fraglist: off
 tcp-segmentation-offload: on
        tx-tcp-segmentation: on
        tx-tcp-ecn-segmentation: on</code></pre>
            <p>The last missing piece we found was that offload changes are applied only during connection initiation, so we restarted Kafka, and we immediately saw a performance improvement (green line):</p>
            <figure>
            <a href="http://staging.blog.mrk.cfdata.org/content/images/2018/04/17.png">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/73uyXt5y4F1L6AUULX8S9g/e80494d09daf7d0b87884c62fd5341e6/17.png" />
            </a>
            </figure><p>Not enabling offload features when possible seems like a pretty bad regression, so we filed a ticket for <code>systemd</code>:</p><ul><li><p><a href="https://github.com/systemd/systemd/issues/6629">https://github.com/systemd/systemd/issues/6629</a></p></li></ul><p>In the meantime, we work around our upstream issue by enabling offload features automatically on boot if they are disabled on VLAN interfaces.</p><p>Having a fix enabled, we rebooted our <code>logs</code> Kafka cluster to upgrade to the latest kernel, and our 5 day CPU usage history yielded positive results:</p>
            <figure>
            <a href="http://staging.blog.mrk.cfdata.org/content/images/2018/04/18.png">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/VBuiRySNEfFiN8LQ9nUg5/a5a1881b229cb1e173663af52f3eb136/18.png" />
            </a>
            </figure><p>The DNS cluster also yielded positive results, with just 2 nodes rebooted (purple line going down):</p>
            <figure>
            <a href="http://staging.blog.mrk.cfdata.org/content/images/2018/04/19.png">
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4CWuJQCmMt7QarvAdU0b3g/c35ad9f7a9ab6113614f736f0e682d64/19.png" />
            </a>
            </figure>
    <div>
      <h3>Conclusion</h3>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>It was an error on our part to hit performance degradation without a good regression framework in place to catch the issue. Luckily, due to our heavy use of version control, we managed to bisect the issue rather quickly, and had a temp rollback in place while root causing the problem.</p><p>In the end, enabling offload also removed RCU stalls. It's not really clear whether it was the cause or just a catalyst, but the end result speaks for itself.</p><p>On the bright side, we dug pretty deep into Linux kernel internals, and although there were fleeting moments of giving up, moving to the woods to become a park ranger, we persevered and came out of the forest successful.</p><hr /><p><i>If deep diving from high level symptoms to kernel/OS issues makes you excited, </i><a href="https://www.cloudflare.com/careers/"><i>drop us a line</i></a><i>.</i></p><hr /> ]]></content:encoded>
            <category><![CDATA[Speed & Reliability]]></category>
            <category><![CDATA[Kafka]]></category>
            <category><![CDATA[eBPF]]></category>
            <category><![CDATA[Linux]]></category>
            <guid isPermaLink="false">29dWe9XJa54DvzHbTBAzEk</guid>
            <dc:creator>Ivan Babrou</dc:creator>
        </item>
        <item>
            <title><![CDATA[eBPF, Sockets, Hop Distance and manually writing eBPF assembly]]></title>
            <link>https://blog.cloudflare.com/epbf_sockets_hop_distance/</link>
            <pubDate>Thu, 29 Mar 2018 10:43:38 GMT</pubDate>
            <description><![CDATA[ A friend gave me an interesting task: extract IP TTL values from TCP connections established by a userspace program. This seemingly simple task quickly exploded into an epic Linux system programming hack.  ]]></description>
            <content:encoded><![CDATA[ <p>A friend gave me an interesting task: extract IP TTL values from TCP connections established by a userspace program. This seemingly simple task quickly exploded into an epic Linux system programming hack. The result code is grossly over engineered, but boy, did we learn plenty in the process!</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1UrWjrMBPW4l3ipvThL0sn/1e78cd221cc63a5fb9964b3bd55cd11d/3845353725_7d7c624f34_z.jpg" />
            
            </figure><p><a href="https://creativecommons.org/licenses/by-sa/2.0/">CC BY-SA 2.0</a> <a href="https://www.flickr.com/photos/paulmiller/3845353725/">image</a> by <a href="https://www.flickr.com/photos/paulmiller">Paul Miller</a></p>
    <div>
      <h3>Context</h3>
      <a href="#context">
        
      </a>
    </div>
    <p>You may wonder why she wanted to inspect the TTL packet field (formally known as "IP Time To Live (TTL)" in IPv4, or "Hop Count" in IPv6)? The reason is simple - she wanted to ensure that the connections are routed outside of our datacenter. The "Hop Distance" - the difference between the TTL value set by the originating machine and the TTL value in the packet received at its destination - shows how many routers the packet crossed. If a packet crossed two or more routers, we know it indeed came from outside of our datacenter.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6eYDA3vNOF8Cc9ytLOPo9N/c4862fac898725e1bd54b0284977f251/Screen-Shot-2018-03-29-at-10.52.49-AM-1.png" />
            
            </figure><p>It's uncommon to look at TTL values (except for their intended purpose of mitigating routing loops by checking when the TTL reaches zero). The normal way to deal with the problem we had would be to blocklist IP ranges of our servers. But it’s not that simple in our setup. Our IP numbering configuration is rather baroque, with plenty of Anycast, Unicast and Reserved IP ranges. Some belong to us, some don't. We wanted to avoid having to maintain a hard-coded blocklist of IP ranges.</p><p>The gist of the idea is: we want to note the TTL value from a returned SYN+ACK packet. Having this number we can estimate the Hop Distance - number of routers on the path. If the Hop Distance is:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1LCtnMJcT3dIXlGwBrWf69/0dd6441edca81092fe6c536203657e21/Screen-Shot-2018-03-29-at-10.50.38-AM.png" />
            
            </figure><ul><li><p><b>zero</b>: we know the connection went to localhost or a local network.</p></li></ul>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2ugFXo390P0VMXOpsvqtVQ/04fc3c2133fbcfaa40db47655b131ab3/Screen-Shot-2018-03-29-at-10.49.42-AM.png" />
            
            </figure><ul><li><p><b>one</b>: connection went through our router, and was terminated just behind it.</p></li></ul>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5dwa36qw1gHyNNLKJ24UiX/a3ee616930c1335d4e6f8dc3fa7da5bb/Screen-Shot-2018-03-29-at-10.49.48-AM.png" />
            
            </figure><ul><li><p><b>two</b>: connection went through two routers. Most possibly our router, and one just near to it.</p></li></ul><p>For our use case, we want to see if the Hop Distance was two or more - this would ensure the connection was routed outside the datacenter.</p>
    <div>
      <h3>Not so easy</h3>
      <a href="#not-so-easy">
        
      </a>
    </div>
    <p>It's easy to read TTL values from a userspace application, right? No. It turns out it's almost impossible. Here are the theoretical options we considered early on:</p><p>A) Run a libpcap/tcpdump-like raw socket, and catch the SYN+ACK's manually. We ruled out this design quickly - it requires elevated privileges. Also, raw sockets are pretty fragile: they can suffer packet loss if the userspace application can’t keep up.</p><p>B) Use the IP_RECVTTL socket option. IP_RECVTTL requests a "cmsg" data to be attached to control/ancillary data in a <code>recvmsg()</code> syscall. This is a good choice for UDP connections, but this socket option is not supported by TCP SOCK_STREAM sockets.</p><p>Extracting the TTL is not so easy.</p>
    <div>
      <h3>SO_ATTACH_FILTER to rule the world!</h3>
      <a href="#so_attach_filter-to-rule-the-world">
        
      </a>
    </div>
    
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1qX4zkQCTMRtJsnaqzysvp/a95be0f638ee764a86591c9576671281/315128991_d49c312fbc_z.jpg" />
            
            </figure><p><a href="https://creativecommons.org/licenses/by-sa/2.0/">CC BY-SA 2.0</a> <a href="https://www.flickr.com/photos/leejordan/315128991/">image</a> by <a href="https://www.flickr.com/photos/leejordan/">Lee Jordan</a></p><p>Wait, there is a third way!</p><p>You see, for quite some time it has been possible to attach a BPF filtering program to a socket. See <a href="http://man7.org/linux/man-pages/man7/socket.7.html"><code>socket(7)</code></a></p>
            <pre><code>SO_ATTACH_FILTER (since Linux 2.2), SO_ATTACH_BPF (since Linux 3.19)
    Attach a classic BPF (SO_ATTACH_FILTER) or an extended BPF
    (SO_ATTACH_BPF) program to the socket for use as a filter of
    incoming packets.  A packet will be dropped if the filter pro‐
    gram returns zero.  If the filter program returns a nonzero
    value which is less than the packet's data length, the packet
    will be truncated to the length returned.  If the value
    returned by the filter is greater than or equal to the
    packet's data length, the packet is allowed to proceed unmodi‐
    fied.</code></pre>
            <p>You probably take advantage of SO_ATTACH_FILTER already: This is how tcpdump/wireshark does filtering when you're dumping packets off the wire.</p><p>How does it work? Depending on the result of a <a href="/bpf-the-forgotten-bytecode/">BPF program</a>, packets can be filtered, truncated or passed to the socket without modification. Normally SO_ATTACH_FILTER is used for RAW sockets, but surprisingly, BPF filters can also be attached to normal SOCK_STREAM and SOCK_DGRAM sockets!</p><p>We don't want to truncate packets though - we want to extract the TTL. Unfortunately with Classical BPF (cBPF) it's impossible to extract any data from a running BPF filter program.</p>
    <div>
      <h3>eBPF and maps</h3>
      <a href="#ebpf-and-maps">
        
      </a>
    </div>
    <p>This changed with modern BPF machinery, which includes:</p><ul><li><p>modernised eBPF bytecode</p></li><li><p>eBPF maps</p></li><li><p><a href="https://man7.org/linux/man-pages/man2/bpf.2.html"><code>bpf()</code> syscall</a></p></li><li><p>SO_ATTACH_BPF socket option</p></li></ul><p>eBPF bytecode can be thought of as an extension to Classical BPF, but it's the extra features that really let it shine.</p><p>The gem is the "map" abstraction. An eBPF map is a thingy that allows an eBPF program to store data and share it with a userspace code. Think of an eBPF map as a data structure (a hash table most usually) shared between a userspace program and an eBPF program running in kernel space.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/51BMjhXrkIIAlAatG0qsMh/bdd6b955f048ae0a2cd5f5005a460bfe/Screen-Shot-2018-03-29-at-10.59.43-AM.png" />
            
            </figure><p>To solve our TTL problem, we can use eBPF filter program. It will look at the TTL values of passing packets, and save them in an eBPF map. Later, we can inspect the eBPF map and analyze the recorded values from userspace.</p>
    <div>
      <h3>SO_ATTACH_BPF to rule the world!</h3>
      <a href="#so_attach_bpf-to-rule-the-world">
        
      </a>
    </div>
    <p>To use eBPF we need a number of things set up. First, we need to create an "eBPF map". There <a href="https://elixir.bootlin.com/linux/v4.15.13/source/include/uapi/linux/bpf.h#L99">are many specialized map types</a>, but for our purposes let's use the "hash" BPF_MAP_TYPE_HASH type.</p><p>We need to figure out the <i>"bpf(BPF_MAP_CREATE, map type, key size, value size, limit, flags)"</i> parameters. For our small TTL program, let's set 4 bytes for key size, and 8 byte value size. The max element limit is set to 5. It doesn't matter, we expect all the packets in one connection to have just one coherent TTL value anyway.</p><p>This is how it would look in a <a href="https://github.com/cloudflare/cloudflare-blog/blob/master/2018-03-ebpf/ebpf.go#L57">Golang code</a>:</p>
            <pre><code>bpfMapFd, err := ebpf.NewMap(ebpf.Hash, 4, 8, 5, 0)</code></pre>
            <p>A word of warning is needed here. BPF maps use the "locked memory" resource. With multiple BPF programs and maps, it's easy to exhaust the default tiny 64 KiB limit. Consider bumping this with <code>ulimit -l</code>, for example:</p>
            <pre><code>ulimit -l 10240</code></pre>
            <p>The <code>bpf()</code> syscall returns a file descriptor pointing to the kernel BPF map we just created. With it handy we can operate on a map. The possible operations are:</p><ul><li><p><code>bpf(BPF_MAP_LOOKUP_ELEM, &lt;key&gt;)</code></p></li><li><p><code>bpf(BPF_MAP_UPDATE_ELEM, &lt;key&gt;, &lt;value&gt;, &lt;flags&gt;)</code></p></li><li><p><code>bpf(BPF_MAP_DELETE_ELEM, &lt;key&gt;)</code></p></li><li><p><code>bpf(BPF_MAP_GET_NEXT_KEY, &lt;key&gt;)</code></p></li></ul><p>More on this later.</p><p>With the map created, we need to create a BPF program. As opposed to classical BPF - where the bytecode was a parameter to SO_ATTACH_FILTER - the bytecode is now loaded by the <code>bpf()</code> syscall. Specifically: <code>bpf(BPF_PROG_LOAD)</code>.</p><p>In our <a href="https://github.com/cloudflare/cloudflare-blog/blob/master/2018-03-ebpf/ebpf.go#L78-L131">Golang program the eBPF program setup</a> looks like:</p>
            <pre><code>ebpfInss := ebpf.Instructions{
	ebpf.BPFIDstOffSrc(ebpf.LdXW, ebpf.Reg0, ebpf.Reg1, 16),
	ebpf.BPFIDstOffImm(ebpf.JEqImm, ebpf.Reg0, 3, int32(htons(ETH_P_IPV6))),
	ebpf.BPFIDstSrc(ebpf.MovSrc, ebpf.Reg6, ebpf.Reg1),
	ebpf.BPFIImm(ebpf.LdAbsB, int32(-0x100000+8)),
...
	ebpf.BPFIDstImm(ebpf.MovImm, ebpf.Reg0, -1),
	ebpf.BPFIOp(ebpf.Exit),
}

bpfProgram, err := ebpf.NewProgram(ebpf.SocketFilter, &amp;ebpfInss, "GPL", 0)</code></pre>
            <p>Writing eBPF by hand is rather controversial. Most people use <code>clang</code> (from version 3.7 onwards) to compile a code written in a C dialect into an eBPF bytecode. The resulting bytecode is saved in an ELF file, which can be loaded by most eBPF libraries. This ELF file also includes description of maps, so you don’t need to set them manually.</p><p>I personally don't see the point in adding an ELF/clang dependency for simple SO_ATTACH_BPF snippets. Don't be afraid of the raw bytecode!</p>
    <div>
      <h3>BPF calling convention</h3>
      <a href="#bpf-calling-convention">
        
      </a>
    </div>
    <p>Before we go further we should highlight couple of things about the eBPF environment. The official kernel documentation isn't too friendly:</p><ul><li><p><a href="https://www.kernel.org/doc/Documentation/networking/filter.txt">Documentation/networking/filter.txt</a></p></li></ul><p>The first important bit to know, is the calling convention:</p><ul><li><p>R0 - return value from in-kernel function, and exit value for eBPF program</p></li><li><p>R1-R5 - arguments from eBPF program to in-kernel function</p></li><li><p>R6-R9 - callee saved registers that in-kernel function will preserve</p></li><li><p>R10 - read-only frame pointer to access stack</p></li></ul><p>When the BPF is started, R1 contains a pointer to <code>ctx</code>. This data structure <a href="https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/bpf.h#L799">is defined as <code>struct __sk_buff</code></a>. For example, to access the <code>protocol</code> field you'd need to run:</p>
            <pre><code>r0 = *(u32 *)(r1 + 16)</code></pre>
            <p>Or in other words:</p>
            <pre><code>ebpf.BPFIDstOffSrc(ebpf.LdXW, ebpf.Reg0, ebpf.Reg1, 16),</code></pre>
            <p>Which is exactly what we do in first line of our program, since we need to choose between IPv4 or IPv6 code branches.</p>
    <div>
      <h3>Accessing the BPF payload</h3>
      <a href="#accessing-the-bpf-payload">
        
      </a>
    </div>
    <p>Next, there are special instructions for packet payload loading. Most BPF programs (but not all!) run in the context of packet filtering, so it makes sense to accelerate data lookups by having magic opcodes for accessing packet data.</p><p>Instead of dereferencing context, like <code>ctx-&gt;data[x]</code> to load a byte, BPF supports the <code>BPF_LD</code> instruction that can do it in one operation. There are caveats though, the documentation says:</p>
            <pre><code>eBPF has two non-generic instructions: (BPF_ABS | &lt;size&gt; | BPF_LD) and
(BPF_IND | &lt;size&gt; | BPF_LD) which are used to access packet data.

They had to be carried over from classic BPF to have strong performance of
socket filters running in eBPF interpreter. These instructions can only
be used when interpreter context is a pointer to 'struct sk_buff' and
have seven implicit operands. Register R6 is an implicit input that must
contain pointer to sk_buff. Register R0 is an implicit output which contains
the data fetched from the packet. Registers R1-R5 are scratch registers
and must not be used to store the data across BPF_ABS | BPF_LD or
BPF_IND | BPF_LD instructions.</code></pre>
            <p>In other words: before calling <code>BPF_LD</code> we must move <code>ctx</code> to R6, like this:</p>
            <pre><code>ebpf.BPFIDstSrc(ebpf.MovSrc, ebpf.Reg6, ebpf.Reg1),</code></pre>
            <p>Then we can call the load:</p>
            <pre><code>ebpf.BPFIImm(ebpf.LdAbsB, int32(-0x100000+7)),</code></pre>
            <p>At this stage the result is in r0, but we must remember the r1-r5 should be considered dirty. For an instruction the <code>BPF_LD</code> looks very much like a function call.</p>
    <div>
      <h3>Magical Layer 3 offset</h3>
      <a href="#magical-layer-3-offset">
        
      </a>
    </div>
    <p>Next note the load offset - we loaded the <code>-0x100000+7</code> byte of the packet. This magic offset is another BPF context curiosity. It turns out that the BPF script loaded under SO_ATTACH_BPF on a SOCK_STREAM (or SOCK_DGRAM) socket, will only see Layer 4 and higher OSI layers by default. To extract the TTL we need access to the layer 3 header (i.e. the IP header). To access L3 in the L4 context, we must offset the data lookups by magical -0x100000.</p><p>This magic constant <a href="https://github.com/torvalds/linux/blob/ead751507de86d90fa250431e9990a8b881f713c/include/uapi/linux/filter.h#L84">is defined in the kernel</a>.</p><p>For completeness, the <code>+7</code> is, of course, the offset of the TTL field in an IPv4 packet. Our small BPF program also supports IPv6 where the TTL/Hop Count is at offset <code>+8</code>.</p>
    <div>
      <h3>Return value</h3>
      <a href="#return-value">
        
      </a>
    </div>
    <p>Finally, the return value of the BPF program is meaningful. In the context of packet filtering it will be interpreted as a truncated packet length.Had we returned 0 - the packet would be dropped and wouldn't be seen by the userspace socket application. It's quite interesting that we can do packet-based data manipulation with eBPF on a stream-based socket. Anyway, our script returns -1, which when cast to unsigned will be interpreted as a very large number:</p>
            <pre><code>ebpf.BPFIDstImm(ebpf.MovImm, ebpf.Reg0, -1),
ebpf.BPFIOp(ebpf.Exit),</code></pre>
            
    <div>
      <h3>Extracting data from map</h3>
      <a href="#extracting-data-from-map">
        
      </a>
    </div>
    <p>Our running BPF program will set a key on our map for any matched packet. The key is the recorded TTL value, the value is the packet count. The value counter is somewhat vulnerable to a tiny race condition, but it's ignorable for our purposes. Later on, to extract the data from userspace program we use this Golang loop:</p>
            <pre><code>var (
	value   MapU64
	k1, k2  MapU32
)

for {
	ok, err := bpfMap.Get(k1, &amp;value, 8)
	if ok {
		// k1 is TTL, value is counter
		...
	}

	ok, err = bpfMap.GetNextKey(k1, &amp;k2, 4)
	if err != nil || ok == false {
		break
	}
	k1 = k2
}</code></pre>
            
    <div>
      <h3>Putting it all together</h3>
      <a href="#putting-it-all-together">
        
      </a>
    </div>
    <p>Now with all the pieces ready we can make it a proper runnable program. There is little point in discussing it here, so allow me to refer to the source code. The BPF pieces are here:</p><ul><li><p><a href="https://github.com/cloudflare/cloudflare-blog/blob/master/2018-03-ebpf/ebpf.go">ebpf.go</a></p></li></ul><p>We haven't discussed how to catch inbound SYN+ACK in the BPF program. This is a matter of setting up BPF before calling <code>connect()</code>. Sadly, it's impossible to customize <code>net.Dial</code> in Golang. Instead we wrote a surprisingly painful and awful custom Dial implementation. The ugly custom dialer code is here:</p><ul><li><p><a href="https://github.com/cloudflare/cloudflare-blog/blob/master/2018-03-ebpf/magic_conn.go">magic_conn.go</a></p></li></ul><p>To run all this you need kernel 4.4+ Kernel with the <code>bpf()</code> syscall compiled in. BPF features of specific kernels are documented in this superb page from BCC:</p><ul><li><p><a href="https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md">docs/kernel-versions.md</a></p></li></ul><p>Run the code to observe the TTL Hop Counts:</p>
            <pre><code>$ ./ttl-ebpf tcp4://google.com:80 tcp6://google.com:80 \
             tcp4://cloudflare.com:80 tcp6://cloudflare.com:80
[+] TTL distance to tcp4://google.com:80 172.217.4.174 is 6
[+] TTL distance to tcp6://google.com:80 [2607:f8b0:4005:809::200e] is 4
[+] TTL distance to tcp4://cloudflare.com:80 198.41.215.162 is 3
[+] TTL distance to tcp6://cloudflare.com:80 [2400:cb00:2048:1::c629:d6a2] is 3</code></pre>
            
    <div>
      <h3>Takeaways</h3>
      <a href="#takeaways">
        
      </a>
    </div>
    <p>In this blog post we dived into the new eBPF machinery, including the <code>bpf()</code> syscall, maps and SO_ATTACH_BPF. This work allowed me to realize the potential of running SO_ATTACH_BPF on fully established TCP sockets. Undoubtedly, eBPF still requires plenty of love and documentation, but it seems to be a perfect bridge to expose low level toggles to userspace applications.</p><p>I highly recommend keeping the dependencies small. For small BPF programs, like the one shown, there is little need for complex clang compilation and ELF loading. Don't be afraid of the eBPF bytecode!</p><p>We only touched on SO_ATTACH_BPF, where we analyzed network packets with BPF running on network sockets. There is more! First, you can attach BPFs to a dozen "things", XDP being the most obvious example. <a href="https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/bpf.h#L119">Full list</a>. Then, it's possible to actually affect kernel packet processing, here is a <a href="https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/bpf.h#L318">full list of helper functions</a>, some of which can modify kernel data structures.</p><p>In February <a href="https://lwn.net/Articles/747551/">LWN jokingly wrote</a>:</p>
            <pre><code>Developers should be careful, though; this could
prove to be a slippery slope leading toward something 
that starts to look like a microkernel architecture.</code></pre>
            <p>There is a grain of truth here. Maybe the ability to run eBPF on variety of subsystems feels like microkernel coding, but definitely the SO_ATTACH_BPF smells like <a href="https://en.wikipedia.org/wiki/STREAMS">STREAMS</a> programming model <a href="https://cseweb.ucsd.edu/classes/fa01/cse221/papers/ritchie-stream-io-belllabs84.pdf">from 1984</a>.</p><hr /><p>Thanks to <a href="https://twitter.com/akajibi">Gilberto Bertin</a> and <a href="https://twitter.com/dwragg">David Wragg</a> for helping out with the eBPF bytecode.</p><hr /><p><i>Doing eBPF work sound interesting? Join our </i><a href="https://boards.greenhouse.io/cloudflare/jobs/589572"><i>world famous team</i></a><i> in London, Austin, San Francisco, Champaign and our elite office in Warsaw, Poland</i>.</p> ]]></content:encoded>
            <category><![CDATA[TTL]]></category>
            <category><![CDATA[TCP]]></category>
            <category><![CDATA[IPv4]]></category>
            <category><![CDATA[IPv6]]></category>
            <category><![CDATA[eBPF]]></category>
            <guid isPermaLink="false">6LTjo1ZkNHiWvy46IuloEd</guid>
            <dc:creator>Marek Majkowski</dc:creator>
        </item>
    </channel>
</rss>