
<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>Sat, 04 Apr 2026 22:12:24 GMT</lastBuildDate>
        <item>
            <title><![CDATA[Random Employee Chats at Cloudflare]]></title>
            <link>https://blog.cloudflare.com/random-employee-chats-cloudflare/</link>
            <pubDate>Sat, 20 Mar 2021 12:00:00 GMT</pubDate>
            <description><![CDATA[ We developed the Random Employee Chats application internally, with the goal of recreating the pre-pandemic informal interactions. Here's how we moved from a shared spreadsheet to Cloudflare Workers to automate the entire process. ]]></description>
            <content:encoded><![CDATA[ <p>Due to the COVID-19 pandemic, most Cloudflare offices closed in March 2020, and employees began working from home. Having online meetings presented its own challenges, but preserving the benefits of casual encounters in physical offices was something we struggled with. Those informal interactions, like teams talking next to the coffee machine, help form the social glue that holds companies together.</p><p>In an attempt to recreate that experience, David Wragg, an engineer at Cloudflare, introduced “Random Engineer Chats” (We’re calling them “Random Employee Chats” here since this can be applied to any team). The idea is that participants are randomly paired, and the pairs then schedule a 30-minute video call. There’s no fixed agenda for these conversations, but the participants might learn what is going on in other teams, gain new perspectives on their own work by discussing it, or meet new people.</p><p>The first iteration of Random Employee Chats used a shared spreadsheet to coordinate the process. People would sign up by adding themselves to the spreadsheet, and once a week, David would randomly form pairs from the list and send out emails with the results. Then, each pair would schedule a call at their convenience. This process was the minimum viable implementation of the idea, but it meant that the process relied on a single person.</p>
    <div>
      <h3>Moving to Cloudflare Workers</h3>
      <a href="#moving-to-cloudflare-workers">
        
      </a>
    </div>
    <p>We wanted to automate these repetitive manual tasks, and naturally, we wanted to use <a href="https://workers.cloudflare.com/">Cloudflare Workers</a> to do it. This is a great example of a complete application that runs entirely in Cloudflare Workers on the edge with no backend or origin server.</p><p>The technical requirements included:</p><ul><li><p>A user interface so people can sign up</p></li><li><p>Storage to keep track of the participants</p></li><li><p>A program that automatically pairs participants and notifies each pair</p></li><li><p>A program that reminds people to register for the next sessions</p></li></ul><p>Workers met all of these requirements, and the resulting application runs in Cloudflare's edge network without any need to run code or store data on other platforms. The Workers script supplies the UI that returns static HTML and JavaScript assets, and for storage, Workers KV keeps track of people who signed in.</p><p>We also recently announced <a href="https://developers.cloudflare.com/workers/platform/cron-triggers">Workers Cron Triggers</a> which allow us to run a Cloudflare Workers script on a defined schedule. The Workers Cron Triggers are perfect for pairing people up before the sessions and reminding users to register for the next session.</p>
    <div>
      <h3>The User Interface</h3>
      <a href="#the-user-interface">
        
      </a>
    </div>
    
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/26ih1wWQgNslI8kNjBYq1d/20676f1eef4c8052cc6ec2dda8c06eb7/Random-Engineer-Chat-Dashboard-1.png" />
            
            </figure><p>The interface is very simple. It shows the list of participants and allows users to register for the next session.</p><p>When a user clicks on the register button, it calls an API that adds a key in Workers KV:</p>
            <pre><code>key: register:ID
value: {"name":"Sven Sauleau","picture":"picture.jpg","email":"example@cloudflare.com"}</code></pre>
            <p>User information is stored in Workers KV and displayed in the interface to create the list of participants. The user information gets deleted during pairing so the list is ready for the next round of chats. We require weekly sign-ups from participants who want to participate in the chats to confirm their availability.</p><p>The code for the interface can be found <a href="https://github.com/cloudflare/random-employee-chat/tree/master/src/workers/randengchat/public">here</a> and the API is <a href="https://github.com/cloudflare/random-employee-chat/blob/master/src/workers/randengchat/server/index.js">here</a>.</p>
    <div>
      <h3>Forming the pairs</h3>
      <a href="#forming-the-pairs">
        
      </a>
    </div>
    <p>A Random Employee Chat is a one-on-one conversation, so at a set time, the application puts participants into pairs. Each Monday morning at 0800 UTC, a Workers cron job runs the pairing script which is deployed using <a href="https://developers.cloudflare.com/workers/cli-wrangler">Wrangler</a>.</p><p>Wrangler supports configuring the schedule for a job using the familiar cron notation. For instance, our wrangler.toml has:</p>
            <pre><code>name = "randengchat-cron-pair"
type = "webpack"
account_id = "..."
webpack_config = "webpack.config.js"
…

kv_namespaces = [...]

[triggers]
crons = ["0 8 * * 2"]</code></pre>
            <p>The pairing script is the most intricate part of the application, so let’s run through its code. First, we list the users that are currently registered. This is done using the <a href="https://developers.cloudflare.com/workers/runtime-apis/kv#listing-by-prefix">list</a> function in Workers KV extracting keys with the prefix <code>register:</code>.</p>
            <pre><code>const list = await KV_NAMESPACE.list({ prefix: "register:" });</code></pre>
            <p>If we don’t have an even number of participants, we remove one person from the list (David!).</p><p>Then, we create all possible pairs and attach a weight to them.</p>
            <pre><code>async function createWeightedPairs() {
  const pairs = [];
  for (let i = 0; i &lt; keys.length - 1; i++) {
    for (let j = i + 1; j &lt; keys.length; j++) {
      const weight = (await countTimesPaired(...)) * -1;
      pairs.push([i, j, weight]);
    }
  }
  return pairs;
}</code></pre>
            <p>For example, suppose four people have registered (Tom, Edie, Ivie and Ada), that’s 6 possible pairs (<a href="https://www.wolframalpha.com/input/?i=4+choose+2">4 choose 2</a>). We might end up with the following pairs and their associated weights:</p>
            <pre><code>(Tom, Edie, 1)
(Tom, Ivie, 0)
(Tom, Ada, 1)
(Edie, Ivie, 2)
(Edie, Ada, 0)
(Ivie, Ada, 2)</code></pre>
            <p>The weight is calculated using the number of times a pair matched in the past to avoid scheduling chats between people that already met. More sophisticated factors could be taken into account, such as the same office or timezone, when they last met, and etc.</p><p>We keep track of how many times the pair matched using a count kept in KV:</p>
            <pre><code>async function countTimesPaired(key) {
  const v = await DB.get(key, "json");
  if (v !== null &amp;&amp; v.count) {
    return v.count;
  }
  return 0;
}</code></pre>
            <p>The people form a complete graph with people as nodes and the edges weighted by the number of times the two people connected by the edge have met.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/anoEru6iUYXNlVFxpCk4w/67d7587b1440044c1a9072545dc8cce6/image5-20.png" />
            
            </figure><p>Next, we run a weighted matching algorithm, in our case the <a href="https://en.wikipedia.org/wiki/Blossom_algorithm">Blossom algorithm</a>, which will find a maximum matching on the graph (a set of edges that maximize the number of pairs of people connected with each person appearing exactly once). As we use the weighted form of the Blossom algorithm we also minimize the path weights. This has the effect of finding the optimal set of pairs minimizing the number of times people have met previously.</p><p>In the case above the algorithm suggests the optimal pairs are  (Tom, Ivie) and (Edie, Ada). In this case, those pairs have never met before.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5Fi6gV8b7uZHZMVR7jouFF/0e7ef8ef4cdcd6dde01e4fb637bc964c/image2-18.png" />
            
            </figure><p>The pairs are recorded in Workers KV with their updated matching count to refine the weights at future sessions:</p>
            <pre><code>key: paired:ID
value: {"emails":["left@cloudflare.com","right@cloudflare.com", "count": 1]}</code></pre>
            <p>A notification is sent to each pair of users to notify them that they matched.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7xVSUWDwEFKqY2KS9Mupwj/0e7e502307c3a67fc6e7d124a08a5101/image4-17.png" />
            
            </figure><p>Once the pairing is done, all <code>register:</code> keys are deleted from KV.</p><p>All the pairing logic is <a href="https://github.com/cloudflare/random-employee-chat/blob/master/src/workers/cron-pair/index.js">here</a>.</p>
    <div>
      <h3>Reminders</h3>
      <a href="#reminders">
        
      </a>
    </div>
    <p>The application sends users a reminder to sign up every week. For the reminder, we use another Workers cron job that runs every Thursday at 1300 UTC. The schedule in Wrangler is</p>
            <pre><code>[triggers]
crons = ["0 13 * * 5"]</code></pre>
            <p>This script is much simpler than the pairing script. It simply sends a message to a room in our company messaging platform that notifies all members of the channel.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7ACIIWbP4COwxXbImfqiQE/78488aeabc86a3a1b6d382ffe32f42a2/image3-19.png" />
            
            </figure><p>All the reminder code is <a href="https://github.com/cloudflare/random-employee-chat/blob/master/src/workers/cron-reminder/index.js">here</a>.</p><p>We hope you find this code useful and that it inspires you to use Workers, Workers KV, Workers Unbound and Workers Cron Triggers to write large, real applications that run entirely without a backend server.</p> ]]></content:encoded>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Cloudflare Workers KV]]></category>
            <category><![CDATA[Wrangler]]></category>
            <guid isPermaLink="false">3MzecqWNHhc0wbjnxE2kiX</guid>
            <dc:creator>Sven Sauleau</dc:creator>
            <dc:creator>David Wragg</dc:creator>
        </item>
        <item>
            <title><![CDATA[Unimog - Cloudflare’s edge load balancer]]></title>
            <link>https://blog.cloudflare.com/unimog-cloudflares-edge-load-balancer/</link>
            <pubDate>Wed, 09 Sep 2020 11:00:00 GMT</pubDate>
            <description><![CDATA[ Unimog is the Layer 4 Load Balancer for Cloudflare’s edge data centers.  This post explains the problems it solves and how it works. ]]></description>
            <content:encoded><![CDATA[ <p>As the scale of Cloudflare’s edge network has grown, we sometimes reach the limits of parts of our architecture. About two years ago we realized that our existing solution for spreading load within our data centers could no longer meet our needs. We embarked on a project to deploy a <i>Layer 4 Load Balancer</i>, internally called <i>Unimog</i>, to improve the reliability and operational efficiency of our edge network. Unimog has now been deployed in production for over a year.</p><p>This post explains the problems Unimog solves and how it works. Unimog builds on techniques used in other Layer 4 Load Balancers, but there are many details of its implementation that are tailored to the needs of our edge network.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/44EsYFcb0zDeaXqYUX6Jxg/3bb5afc4bd624ccb66e53cd091b8bed9/image3-1.png" />
            
            </figure>
    <div>
      <h3>The role of Unimog in our edge network</h3>
      <a href="#the-role-of-unimog-in-our-edge-network">
        
      </a>
    </div>
    <p>Cloudflare operates an anycast network, meaning that our data centers in <a href="https://www.cloudflare.com/network/">200+ cities</a> around the world serve the same IP addresses. For example, our own cloudflare.com website uses Cloudflare services, and one of its IP addresses is 104.17.175.85. All of our data centers will accept connections to that address and respond to HTTP requests. By the magic of Internet routing, when you visit cloudflare.com and your browser connects to 104.17.175.85, your connection will usually go to the closest (and therefore fastest) data center.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4ADAIlOzgQE4V4C7yHERdq/5ae28f8121ca6d39d79acc1a73733d42/internet-2.png" />
            
            </figure><p>Inside those data centers are many servers. The number of servers in each varies greatly (the biggest data centers have a hundred times more servers than the smallest ones). The servers run the <a href="https://www.cloudflare.com/application-services/">application services</a> that implement our products (our caching, DNS, WAF, DDoS mitigation, Spectrum, WARP, etc). Within a single data center, any of the servers can handle a connection for any of our services on any of our anycast IP addresses. This uniformity keeps things simple and avoids bottlenecks.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1653XjnZj666YAdzKgPcCN/d0146fc7d280e556ca4d36cfddbdd5ca/colo-4.png" />
            
            </figure><p>But if any server within a data center can handle any connection, when a connection arrives from a browser or some other client, what controls which server it goes to? That’s the job of Unimog.</p><p>There are two main reasons why we need this control. The first is that we regularly move servers in and out of operation, and servers should only receive connections when they are in operation. For example, we sometimes remove a server from operation in order to perform maintenance on it. And sometimes servers are automatically removed from operation because health checks indicate that they are not functioning correctly.</p><p>The second reason concerns the management of the load on the servers (by load we mean the amount of computing work each one needs to do). If the load on a server exceeds the capacity of its hardware resources, then the quality of service to users will suffer. The performance experienced by users degrades as a server approaches saturation, and if a server becomes sufficiently overloaded, users may see errors. We also want to prevent servers being underloaded, which would reduce the value we get from our investment in hardware. So Unimog ensures that the load is spread across the servers in a data center. This general idea is called load balancing (<i>balancing</i> because the work has to be done somewhere, and so for the load on one server to go down, the load on some other server must go up).</p><p>Note that in this post, we’ll discuss how Cloudflare balances the load on its own servers in edge data centers. But load balancing is a requirement that occurs in many places in distributed computing systems. Cloudflare also has a Layer 7 <a href="https://www.cloudflare.com/load-balancing/">Load Balancing</a> product to allow our customers to balance load across their servers. And Cloudflare uses load balancing in other places <a href="/high-availability-load-balancers-with-maglev/">internally</a>.</p><p>Deploying Unimog led to a big improvement in our ability to balance the load on our servers in our edge data centers. Here’s a chart for one data center, showing the difference due to Unimog. Each line shows the processor utilization of an individual server (the colour of the lines indicates <a href="/cloudflares-gen-x-servers-for-an-accelerated-future/">server model</a>). The load on the servers varies during the day with the activity of users close to this data center. The white line marks the point when we enabled Unimog. You can see that after that point, the load on the servers became much more uniform. We saw similar results when we deployed Unimog to our other data centers.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6ysIFd2DX2oOFyrujMX8OV/a66dc6b3fdabe9c3015887fae118efcb/image1.png" />
            
            </figure>
    <div>
      <h2>How Unimog compares to other load balancers</h2>
      <a href="#how-unimog-compares-to-other-load-balancers">
        
      </a>
    </div>
    <p>There are a variety of techniques for load balancing. Unimog belongs to a category called <i>Layer 4 Load Balancers (L4LBs)</i>. L4LBs direct packets on the network by inspecting information up to layer 4 of the OSI network model, which distinguishes them from the more common <i>Layer 7 Load Balancers</i>.</p><p>The advantage of L4LBs is their efficiency. They direct packets without processing the payload of those packets, so they avoid the overheads associated with higher level protocols. For any load balancer, it’s important that the resources consumed by the load balancer are low compared to the resources devoted to useful work. At Cloudflare, we already pay close attention to the efficient implementation of our services, and that sets a high bar for the load balancer that we put in front of those services.</p><p>The downside of L4LBs is that they can only control which connections go to which servers. They cannot modify the data going over the connection, which prevents them from participating in higher-level protocols like TLS, HTTP, etc. (in contrast, Layer 7 Load Balancers act as proxies, so they can modify data on the connection and participate in those higher-level protocols).</p><p>L4LBs are not new. They are mostly used at companies which have scaling needs that would be hard to meet with L7LBs alone. Google has published about <a href="https://research.google.com/pubs/archive/44824.pdf">Maglev</a>, Facebook open-sourced <a href="https://engineering.fb.com/open-source/open-sourcing-katran-a-scalable-network-load-balancer/">Katran</a>, and Github has open-sourced their <a href="https://github.blog/2018-08-08-glb-director-open-source-load-balancer/">GLB</a>.</p><p>Unimog is the L4LB that Cloudflare has built to meet the needs of our edge network. It shares features with other L4LBs, and it is particularly strongly influenced by GLB. But there are some requirements that were not well-served by existing L4LBs, leading us to build our own:</p><ul><li><p>Unimog is designed to run on the same general-purpose servers that provide application services, rather than requiring a separate tier of servers dedicated to load balancing.</p></li><li><p>It performs dynamic load balancing: measurements of server load are used to adjust the number of connections going to each server, in order to accurately balance load.</p></li><li><p>It supports long-lived connections that remain established for days.</p></li><li><p>Virtual IP addresses are managed as ranges (Cloudflare serves hundreds of thousands of IPv4 addresses on behalf of our customers, so it is impractical to configure these individually).</p></li><li><p>Unimog is tightly integrated with our existing DDoS mitigation system, and the implementation relies on the same XDP technology in the Linux kernel.</p></li></ul><p>The rest of this post describes these features and the design and implementation choices that follow from them in more detail.</p><p>For Unimog to balance load, it’s not enough to send the same (or approximately the same) number of connections to each server, because the performance of our servers varies. We regularly update our server hardware, and we’re now on our <a href="/cloudflares-gen-x-servers-for-an-accelerated-future/">10th generation</a>. Once we deploy a server, we keep it in service for as long as it is cost effective, and the lifetime of a server can be several years. It’s not unusual for a single data center to contain a mix of server models, due to expansion and upgrades over time. Processor performance has increased significantly across our server generations. So within a single data center, we need to send different numbers of connections to different servers to utilize the same percentage of their capacity.</p><p>It’s also not enough to give each server a fixed share of connections based on static estimates of their capacity. Not all connections consume the same amount of CPU. And there are other activities running on our servers and consuming CPU that are not directly driven by connections from clients. So in order to accurately balance load across servers, Unimog does <i>dynamic load balancing</i>: it takes regular measurements of the load on each of our servers, and uses a control loop that increases or decreases the number of connections going to each server so that their loads converge to an appropriate value.</p>
    <div>
      <h2>Refresher: TCP connections</h2>
      <a href="#refresher-tcp-connections">
        
      </a>
    </div>
    <p>The relationship between TCP packets and connections is central to the operation of Unimog, so we’ll briefly describe that relationship.</p><p>(Unimog supports UDP as well as TCP, but for clarity most of this post will focus on the TCP support. We explain how UDP support differs towards the end.)</p><p>Here is the outline of a TCP packet:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1uN86MCb8Xmcxhd8Eupv0R/5c82ac004e4fe1f80c6ba68ea49186d0/image12.png" />
            
            </figure><p>The TCP connection that this packet belongs to is identified by the four labelled header fields, which span the IPv4/IPv6 (i.e. layer 3) and TCP (i.e. layer 4) headers: the source and destination addresses, and the source and destination ports. Collectively, these four fields are known as the 4-tuple. When we say the Unimog sends a connection to a server, we mean that all the packets with the 4-tuple identifying that connection are sent to that server.</p><p>A TCP connection is established via a three-way handshake between the client and the server handling that connection. Once a connection has been established, it is crucial that all the incoming packets for that connection go to that same server. If a TCP packet belonging to the connection is sent to a different server, it will signal the fact that it doesn’t know about the connection to the client with a TCP RST (reset) packet. Upon receiving this notification, the client terminates the connection, probably resulting in the user seeing an error. So a misdirected packet is much worse than a dropped packet. As usual, we consider the network to be unreliable, and it’s fine for occasional packets to be dropped. But even a single misdirected packet can lead to a broken connection.</p><p>Cloudflare handles a wide variety of connections on behalf of our customers. Many of these connections carry HTTP, and are typically short lived. But some HTTP connections are used for websockets, and can remain established for hours or days. Our Spectrum product supports arbitrary TCP connections. TCP connections can be terminated or stall for <a href="/when-tcp-sockets-refuse-to-die/">many reasons</a>, and ideally all applications that use long-lived connections would be able to reconnect transparently, and applications would be designed to support such reconnections. But not all applications and protocols meet this ideal, so we strive to maintain long-lived connections. Unimog can maintain connections that last for many days.</p>
    <div>
      <h3>Forwarding packets</h3>
      <a href="#forwarding-packets">
        
      </a>
    </div>
    <p>The previous section described that the function of Unimog is to steer connections to servers. We’ll now explain how this is implemented.</p><p>To start with, let’s consider how one of our data centers might look without Unimog or any other load balancer. Here’s a conceptual view:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1sGaljcZUwny7RxYvQzF0z/4d15981b1a6d5442cadf99c8c789a857/colo-simple.png" />
            
            </figure><p>Packets arrive from the Internet, and pass through the router, which forwards them on to servers (in reality there is usually additional network infrastructure between the router and the servers, but it doesn’t play a significant role here so we’ll ignore it).</p><p>But is such a simple arrangement possible? Can the router spread traffic over servers without some kind of load balancer in between? Routers have a feature called ECMP (equal cost multipath) routing. Its original purpose is to allow traffic to be spread across multiple paths between two locations, but it is commonly repurposed to spread traffic across multiple servers within a data center. In fact, Cloudflare relied on ECMP alone to spread load across servers before we deployed Unimog. ECMP uses a hashing scheme to ensure that packets on a given connection use the same path (Unimog also employs a hashing scheme, so we’ll discuss how this can work in further detail below) . But ECMP is vulnerable to changes in the set of active servers, such as when servers go in and out of service. These changes cause rehashing events, which break connections to all the servers in an ECMP group. Also, routers impose limits on the sizes of ECMP groups, which means that a single ECMP group cannot cover all the servers in our larger edge data centers. Finally, ECMP does not allow us to do dynamic load balancing by adjusting the share of connections going to each server. These drawbacks mean that ECMP alone is not an effective approach.</p><p>Ideally, to overcome the drawbacks of ECMP, we could program the router with the appropriate logic to direct connections to servers in the way we want. But although programmable network data planes have been a hot research topic in recent years, commodity routers are still essentially fixed-function devices.</p><p>We can work around the limitations of routers by having the router send the packets to some load balancing servers, and then programming those load balancers to forward packets as we want. If the load balancers all act on packets in a consistent way, then it doesn’t matter which load balancer gets which packets from the router (so we can use ECMP to spread packets across the load balancers). That suggests an arrangement like this:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1QsaXd0zJOinv3P3kXgFRm/9baef06d0e1fe4e8106bc1029288329e/colo-lb.png" />
            
            </figure><p>And indeed L4LBs are often deployed like this.</p><p>Instead, Unimog makes <i>every</i> server into a load balancer. The router can send any packet to any server, and that initial server will forward the packet to the right server for that connection:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/77gUuMMXWbIGKclOV82dLp/f89ee9080e094307f1a8b0e8a0a01e6d/colo-unimog.png" />
            
            </figure><p>We have two reasons to favour this arrangement:</p><p>First, in our edge network, we avoid specialised roles for servers. We run the same software stack on the servers in our edge network, providing all of our product features, whether <a href="https://www.cloudflare.com/learning/ddos/how-to-prevent-ddos-attacks/">DDoS attack prevention</a>, website performance features, Cloudflare Workers, WARP, etc. This uniformity is key to the efficient operation of our edge network: we don’t have to manage how many load balancers we have within each of our data centers, because all of our servers act as load balancers.</p><p>The second reason relates to stopping attacks. Cloudflare’s edge network is the target of incessant attacks. Some of these attacks are volumetric - large packet floods which attempt to overwhelm the ability of our data centers to process network traffic from the Internet, and so impact our ability to service legitimate traffic. To successfully mitigate such attacks, it’s important to filter out attack packets as early as possible, minimising the resources they consume. This means that our attack mitigation system needs to occur <i>before</i> the forwarding done by Unimog. That mitigation system is called l4drop, and <a href="/l4drop-xdp-ebpf-based-ddos-mitigations/">we’ve written about it before</a>. l4drop and Unimog are closely integrated. Because l4drop runs on all of our servers, and because l4drop comes before Unimog, it’s natural for Unimog to run on all of our servers too.</p>
    <div>
      <h3>XDP and xdpd</h3>
      <a href="#xdp-and-xdpd">
        
      </a>
    </div>
    <p>Unimog implements packet forwarding using a Linux kernel facility called <a href="https://en.wikipedia.org/wiki/Express_Data_Path"><i>XDP</i></a>. XDP allows a program to be attached to a network interface, and the program gets run for every packet that arrives, before it is processed by the kernel’s main network stack. The XDP program returns an action code to tell the kernel what to do with the packet:</p><ul><li><p>PASS: Pass the packet on to the kernel’s network stack for normal processing.</p></li><li><p>DROP: Drop the packet. This is the basis for l4drop.</p></li><li><p>TX: Transmit the packet back out of the network interface. The XDP program can modify the packet data before transmission. This action is the basis for Unimog forwarding.</p></li></ul><p>XDP programs run within the kernel, making this an efficient approach even at <a href="/how-to-drop-10-million-packets/">high packet rates</a>. XDP programs are expressed as eBPF bytecode, and run within an in-kernel virtual machine. Upon loading an XDP program, the kernel compiles its eBPF code into machine code. The kernel also verifies the program to check that it does not compromise security or stability. eBPF is not only used in the context of XDP: many recent Linux kernel innovations employ eBPF, as it provides a convenient and efficient way to extend the behaviour of the kernel.</p><p>XDP is much more convenient than alternative approaches to packet-level processing, particularly in our context where the servers involved also have many other tasks. We have continued to enhance Unimog since its initial deployment. Our deployment model for new versions of our Unimog XDP code is essentially the same as for userspace services, and we are able to deploy new versions on a weekly basis if needed. Also, established techniques for optimizing the performance of the Linux network stack provide good performance for XDP.</p><p>There are two main alternatives for efficient packet-level processing:</p><ul><li><p>Kernel-bypass networking (such as <a href="https://www.dpdk.org/">DPDK</a>), where a program in userspace manages a network interface (or some part of one) directly without the involvement of the kernel. This approach works best when servers can be dedicated to a network function (due to the need to dedicate processor or network interface hardware resources, and awkward integration with the normal kernel network stack; <a href="/kernel-bypass/">see our old post about this</a>). But we avoid putting servers in specialised roles. (Github’s open-source GLB uses DPDK, and this is one of the main factors that made GLB unsuitable for us.)</p></li><li><p>Kernel modules, where code is added to the kernel to perform the necessary network functions. The Linux IPVS (IP Virtual Server) subsystem falls into this category. But developing, testing, and deploying kernel modules is cumbersome compared to XDP.</p></li></ul><p>The following diagram shows an overview of our use of XDP. Both l4drop and Unimog are implemented by an XDP program. l4drop matches attack packets, and uses the DROP action to discard them. Unimog forwards packets, using the TX action to resend them. Packets that are not dropped or forwarded pass through to the normal Linux network stack. To support our elaborate use of XDP, we have developed the <i>xdpd daemon</i> which performs the necessary supervisory and support functions for our XDP programs.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/mwuvfgu1KFhyMtYkI7c4M/f1450237c9417b357b3c11144f90f048/xdpd.png" />
            
            </figure><p>Rather than a single XDP program, we have a chain of XDP programs that must be run for each packet (l4drop, Unimog, and others we have not covered here). One of the responsibilities of xdpd is to prepare these programs, and to make the appropriate system calls to load them and assemble the full chain.</p><p>Our XDP programs come from two sources. Some are developed in a conventional way: engineers write C code, our build system compiles it (with clang) to eBPF ELF files, and our release system deploys those files to our servers. Our Unimog XDP code works like this. In contrast, the l4drop XDP code is dynamically generated by xdpd based on information it receives from attack detection systems.</p><p>xdpd has many other duties to support our use of XDP:</p><ul><li><p>XDP programs can be supplied with data using data structures called <i>maps</i>. xdpd populates the maps needed by our programs, based on information received from control planes.</p></li><li><p>Programs (for instance, our Unimog XDP program) may depend upon configuration values which are fixed while the program runs, but do not have universal values known at the time their C code was compiled. It would be possible to supply these values to the program via maps, but that would be inefficient (retrieving a value from a map requires a call to a helper function). So instead, xdpd will fix up the eBPF program to insert these constants before it is loaded.</p></li><li><p>Cloudflare carefully monitors the behaviour of all our software systems, and this includes our XDP programs: They emit metrics (via another use of maps), which xdpd exposes to our metrics and alerting system (prometheus).</p></li><li><p>When we deploy a new version of xdpd, <a href="/graceful-upgrades-in-go">it gracefully upgrades</a> in such a way that there is no interruption to the operation of Unimog or l4drop.</p></li></ul><p>Although the XDP programs are written in C, xdpd itself is written in Go. Much of its code is specific to Cloudflare. But in the course of developing xdpd, we have collaborated with Cilium to develop <a href="https://github.com/cilium/ebpf">https://github.com/cilium/ebpf</a>, an open source Go library that provides the operations needed by xdpd for manipulating and loading eBPF programs and related objects. We’re also collaborating with the Linux eBPF community to share our experience, and extend the core eBPF technology in ways that make features of xdpd obsolete.</p><p>In evaluating the performance of Unimog, our main concern is efficiency: that is, the resources consumed for load balancing relative to the resources used for customer-visible services. Our measurements show that Unimog costs less than 1% of the processor utilization, compared to a scenario where no load balancing is in use. Other L4LBs, intended to be used with servers dedicated to load balancing, may place more emphasis on maximum throughput of packets. Nonetheless, our experience with Unimog and XDP in general indicates that the throughput is more than adequate for our needs, even during large volumetric attacks.</p><p>Unimog is not the first L4LB to use XDP. In 2018, <a href="https://engineering.fb.com/open-source/open-sourcing-katran-a-scalable-network-load-balancer/">Facebook open sourced Katran</a>, their XDP-based L4LB data plane. We considered the possibility of reusing code from Katran. But it would not have been worthwhile: the core C code needed to implement an XDP-based L4LB is relatively modest (about 1000 lines of C, both for Unimog and Katran). Furthermore, we had requirements that were not met by Katran, and we also needed to integrate with existing components and systems at Cloudflare (particularly l4drop). So very little of the code could have been reused as-is.</p>
    <div>
      <h2>Encapsulation</h2>
      <a href="#encapsulation">
        
      </a>
    </div>
    <p>As discussed as the start of this post, clients make connections to one of our edge data centers with a destination IP address that can be served by any one of our servers. These addresses that do not correspond to a specific server are known as <i>virtual IPs</i> (VIPs). When our Unimog XDP program forwards a packet destined to a VIP, it must replace that VIP address with the <i>direct IP (DIP)</i> of the appropriate server for the connection, so that when the packet is retransmitted it will reach that server. But it is not sufficient to overwrite the VIP in the packet headers with the DIP, as that would hide the original destination address from the server handling the connection (the original destination address is often needed to correctly handle the connection).</p><p>Instead, the packet must be <i>encapsulated</i>: Another set of packet headers is prepended to the packet, so that the original packet becomes the payload in this new packet. The DIP is then used as the destination address in the outer headers, but the addressing information in the headers of the original packet is preserved. The encapsulated packet is then retransmitted. Once it reaches the target server, it must be <i>decapsulated</i>: the outer headers are stripped off to yield the original packet as if it had arrived directly.</p><p>Encapsulation is a general concept in computer networking, and is used in a variety of contexts. The headers to be added to the packet by encapsulation are defined by an <i>encapsulation format</i>. Many different encapsulation formats have been defined within the industry, tailored to the requirements in specific contexts. Unimog uses a format called <a href="https://www.ietf.org/id/draft-ietf-intarea-gue-09.txt"><i>GUE</i> (Generic UDP Encapsulation)</a>, in order to allow us to re-use the glb-redirect component from github’s GLB (glb-redirect is discussed below).</p><p>GUE is a relatively simple encapsulation format. It encapsulates within a UDP packet, placing a GUE-specific header between the outer IP/UDP headers and the payload packet to allow extension data to be carried (and we’ll see how Unimog takes advantage of this):</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/ImbRfgHVop1QjM0K3R6Lr/8f7ce8fdab72ee2dfacc454d59bc1171/image8.png" />
            
            </figure><p>When an encapsulated packet arrives at a server, the encapsulation process must be reversed. This step is called <i>decapsulation</i>. The headers that were added during the encapsulation process are removed, leaving the original packet to be processed by the network stack as if it had arrived directly from the client.</p><p>An issue that can arise with encapsulation is hitting limits on the maximum packet size, because the encapsulation process makes packets larger. The de-facto maximum packet size on the Internet is 1500 bytes, and not coincidentally this is also the maximum packet size on ethernet networks. For Unimog, encapsulating a 1500-byte packet results in a 1536-byte packet. To allow for these enlarged encapsulated packets, we have enabled jumbo frames on the networks inside our data centers, so that the 1500-byte limit only applies to packets headed out to the Internet.</p>
    <div>
      <h2>Forwarding logic</h2>
      <a href="#forwarding-logic">
        
      </a>
    </div>
    <p>So far, we have described the technology used to implement the Unimog load balancer, but not how our Unimog XDP program selects the DIP address when forwarding a packet. This section describes the basic scheme. But as we’ll see, there is a problem, so then we’ll describe how this scheme is elaborated to solve that problem.</p><p>In outline, our Unimog XDP program processes each packet in the following way:</p><ol><li><p>Determine whether the packet is destined for a VIP address. Not all of the packets arriving at a server are for VIP addresses. Other packets are passed through for normal handling by the kernel’s network stack. (xdpd obtains the VIP address ranges from the Unimog control plane.)</p></li><li><p>Determine the DIP for the server handling the packet’s connection.</p></li><li><p>Encapsulate the packet, and retransmit it to the DIP.</p></li></ol><p>In step 2, note that all the load balancers must act consistently - when forwarding packets, they must all agree about which connections go to which servers. The rate of new connections arriving at a data center is large, so it’s not practical for load balancers to agree by communicating information about connections amongst themselves. Instead L4LBs adopt designs which allow the load balancers to reach consistent forwarding decisions independently. To do this, they rely on hashing schemes: Take the 4-tuple identifying the packet’s connection, put it through a hash function to obtain a key (the hash function ensures that these key values are uniformly distributed), then perform some kind of lookup into a data structure to turn the key into the DIP for the target server.</p><p>Unimog uses such a scheme, with a data structure that is simple compared to some other L4LBs. We call this data structure the <i>forwarding table</i>, and it consists of an array where each entry contains a DIP specifying the target server for the relevant packets (we call these entries <i>buckets</i>). The forwarding table is generated by the Unimog control plane and broadcast to the load balancers (more on this below), so that it has the same contents on all load balancers.</p><p>To look up a packet’s key in the forwarding table, the low N bits from the key are used as the index for a bucket (the forwarding table is always a power-of-2 in size):</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/PTRbsbXdTdzDPg0zTNtkJ/c22c4634097466ff8e57844465713398/hashing.png" />
            
            </figure><p>Note that this approach does not provide per-connection control - each bucket typically applies to many connections. All load balancers in a data center use the same forwarding table, so they all forward packets in a consistent manner. This means it doesn’t matter which packets are sent by the router to which servers, and so ECMP re-hashes are a non-issue. And because the forwarding table is immutable and simple in structure, lookups are fast.</p><p>Although the above description only discusses a single forwarding table, Unimog supports multiple forwarding tables, each one associated with a <i>trafficset</i> - the traffic destined for a particular service. Ranges of VIP addresses are associated with a trafficset. Each trafficset has its own configuration settings and forwarding tables. This gives us the flexibility to differentiate how Unimog behaves for different services.</p><p>Precise load balancing requires the ability to make fine adjustments to the number of connections arriving at each server. So we make the number of buckets in the forwarding table more than 100 times the number of servers. Our data centers can contain hundreds of servers, and so it is normal for a Unimog forwarding table to have tens of thousands of buckets. The DIP for a given server is repeated across many buckets in the forwarding table, and by increasing or decreasing the number of buckets that refer to a server, we can control the share of connections going to that server. Not all buckets will correspond to exactly the same number of connections at a given point in time (the properties of the hash function make this a statistical matter). But experience with Unimog has demonstrated that the relationship between the number of buckets and resulting server load is sufficiently strong to allow for good load balancing.</p><p>But as mentioned, there is a problem with this scheme as presented so far. Updating a forwarding table, and changing the DIPs in some buckets, would break connections that hash to those buckets (because packets on those connections would get forwarded to a different server after the update). But one of the requirements for Unimog is to allow us to change which servers get new connections without impacting the existing connections. For example, sometimes we want to drain the connections to a server, maintaining the existing connections to that server but not forwarding new connections to it, in the expectation that many of the existing connections will terminate of their own accord. The next section explains how we fix this scheme to allow such changes.</p>
    <div>
      <h2>Maintaining established connections</h2>
      <a href="#maintaining-established-connections">
        
      </a>
    </div>
    <p>To make changes to the forwarding table without breaking established connections, Unimog adopts the “daisy chaining” technique described in the paper <a href="https://www.usenix.org/system/files/conference/nsdi18/nsdi18-olteanu.pdf"><i>Stateless Datacenter Load-balancing with Beamer</i></a>.</p><p>To understand how the Beamer technique works, let’s look at what can go wrong when a forwarding table changes: imagine the forwarding table is updated so that a bucket which contained the DIP of server A now refers to server B. A packet that would formerly have been sent to A by the load balancers is now sent to B. If that packet initiates a new connection (it’s a TCP SYN packet), there’s no problem - server B will continue the three-way handshake to complete the new connection. On the other hand, if the packet belongs to a connection established before the change, then the TCP implementation of server B has no matching TCP socket, and so sends a RST back to the client, breaking the connection.</p><p>This explanation hints at a solution: the problem occurs when server B receives a forwarded packet that does not match a TCP socket. If we could change its behaviour in this case to forward the packet a second time to the DIP of server A, that would allow the connection to server A to be preserved. For this to work, server B needs to know the DIP for the bucket <i>before</i> the change.</p><p>To accomplish this, we extend the forwarding table so that each bucket has two slots, each containing the DIP for a server. The first slot contains the current DIP, which is used by the load balancer to forward packets as discussed (and here we refer to this forwarding as the <i>first hop</i>). The second slot preserves the previous DIP (if any), in order to allow the packet to be forwarded again on a <i>second hop</i> when necessary.</p><p>For example, imagine we have a forwarding table that refers to servers A, B, and C, and then it is updated to stop new connections going to server A, but maintaining established connections to server A. This is achieved by replacing server A’s DIP in the first slot of any buckets where it appears, but preserving it in the second slot:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7hTdYu2s3dFhNKhsASADKa/44fe175c6f406753d82390f7a046669f/image9-1.png" />
            
            </figure><p>In addition to extending the forwarding table, this approach requires a component on each server to forward packets on the second hop when necessary. This diagram shows where this <i>redirector</i> fits into the path a packet can take:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6f5CQLIjy5hO2eWKW8i36H/ab18994ccdf15961e8a87d1e3890d2da/sausage.png" />
            
            </figure><p>The redirector follows some simple logic to decide whether to process a packet locally on the first-hop server or to forward it on the second-hop server:</p><ul><li><p>If the packet is a SYN packet, initiating a new connection, then it is always processed by the first-hop server. This ensures that new connections go to the first-hop server.</p></li><li><p>For other packets, the redirector checks whether the packet belongs to a connection with a corresponding TCP socket on the first-hop server. If so, it is processed by that server.</p></li><li><p>Otherwise, the packet has no corresponding TCP socket on the first-hop server. So it is forwarded on to the second-hop server to be processed there (in the expectation that it belongs to some connection established on the second-hop server that we wish to maintain).</p></li></ul><p>In that last step, the redirector needs to know the DIP for the second hop. To avoid the need for the redirector to do forwarding table lookups, the second-hop DIP is placed into the encapsulated packet by the Unimog XDP program (which already does a forwarding table lookup, so it has easy access to this value). This second-hop DIP is carried in a GUE extension header, so that it is readily available to the redirector if it needs to forward the packet again.</p><p>This second hop, when necessary, does have a cost. But in our data centers, the fraction of forwarded packets that take the second hop is usually less than 1% (despite the significance of long-lived connections in our context). The result is that the practical overhead of the second hops is modest.</p><p>When we initially deployed Unimog, we adopted the <a href="https://github.com/github/glb-director/tree/master/src/glb-redirect">glb-redirect iptables module</a> from github’s GLB to serve as the redirector component. In fact, some implementation choices in Unimog, such as the use of GUE, were made in order to facilitate this re-use. glb-redirect worked well for us initially, but subsequently we wanted to enhance the redirector logic. glb-redirect is a custom Linux kernel module, and developing and deploying changes to kernel modules is more difficult for us than for eBPF-based components such as our XDP programs. This is not merely due to Cloudflare having invested more engineering effort in software infrastructure for eBPF; it also results from the more explicit boundary between the kernel and eBPF programs (for example, we are able to run the same eBPF programs on a range of kernel versions without recompilation). We wanted to achieve the same ease of development for the redirector as for our XDP programs.</p><p>To that end, we decided to write an eBPF replacement for glb-redirect. While the redirector could be implemented within XDP, like our load balancer, practical concerns led us to implement it as a <a href="https://man7.org/linux/man-pages/man8/tc-bpf.8.html">TC classifier program</a> instead (TC is the traffic control subsystem within the Linux network stack). A downside to XDP is that the packet contents prior to processing by the XDP program <a href="/xdpcap/">are not visible using conventional tools such as tcpdump</a>, complicating debugging. TC classifiers do not have this downside, and in the context of the redirector, which passes most packets through, the performance advantages of XDP would not be significant.</p><p>The result is cls-redirect, a redirector implemented as a TC classifier program. We have contributed our <a href="https://github.com/torvalds/linux/blob/c4ba153b6501fa7ccfdc7e57946fb1d6011e36e8/tools/testing/selftests/bpf/progs/test_cls_redirect.c">cls-redirect code as part of the Linux kernel test suite</a>. In addition to implementing the redirector logic, cls-redirect also implements decapsulation, removing the need to separately configure GUE tunnel endpoints for this purpose.</p><p>There are some features suggested in the Beamer paper that Unimog does not implement:</p><ul><li><p>Beamer embeds <i>generation numbers</i> in the encapsulated packets to address a potential corner case where a ECMP rehash event occurs at the same time as a forwarding table update is propagating from the control plane to the load balancers. Given the combination of circumstances required for a connection to be impacted by this issue, we believe that in our context the number of affected connections is negligible, and so the added complexity of the generation numbers is not worthwhile.</p></li><li><p>In the Beamer paper, the concept of daisy-chaining encompasses third hops etc. to preserve connections across a series of changes to a bucket. Unimog only uses two hops (the first and second hops above), so in general it can only preserve connections across a single update to a bucket. But our experience is that even with only two hops, a careful strategy for updating the forwarding tables permits connection lifetimes of days.</p></li></ul><p>To elaborate on this second point: when the control plane is updating the forwarding table, it often has some choice in which buckets to change, depending on the event that led to the update. For example, if a server is being brought into service, then some buckets must be assigned to it (by placing the DIP for the new server in the first slot of the bucket). But there is a choice about <i>which</i> buckets. A strategy of choosing the least-recently modified buckets will tend to minimise the impact to connections.</p><p>Furthermore, when updating the forwarding table to adjust the balance of load between servers, Unimog often uses a novel trick: due to the redirector logic, exchanging the first-hop and second-hop DIPs for a bucket only affects which server receives new connections for that bucket, and never impacts any established connections. Unimog is able to achieve load balancing in our edge data centers largely through forwarding table changes of this type.</p>
    <div>
      <h2>Control plane</h2>
      <a href="#control-plane">
        
      </a>
    </div>
    <p>So far, we have discussed the Unimog data plane - the part that processes network packets. But much of the development effort on Unimog has been devoted to the control plane - the part that generates the forwarding tables used by the data plane. In order to correctly maintain the forwarding tables, the control plane consumes information from multiple sources:</p><ul><li><p>Server information: Unimog needs to know the set of servers present in a data center, some key information about each one (such as their DIP addresses), and their operational status. It also needs signals about transitional states, such as when a server is being withdrawn from service, in order to gracefully drain connections (preventing the server from receiving new connections, while maintaining its established connections).</p></li><li><p>Health: Unimog should only send connections to servers that are able to correctly handle those connections, otherwise those servers should be removed from the forwarding tables. To ensure this, it needs health information at the node level (indicating that a server is available) and at the service level (indicating that a service is functioning normally on a server).</p></li><li><p>Load: in order to balance load, Unimog needs information about the resource utilization on each server.</p></li><li><p>IP address information: Cloudflare serves hundreds of thousands of IPv4 addresses, and these are something that we have to treat as a dynamic resource rather than something statically configured.</p></li></ul><p>The control plane is implemented by a process called the <i>conductor</i>. In each of our edge data centers, there is one active conductor, but there are also standby instances that will take over if the active instance goes away.</p><p>We use <a href="https://www.consul.io/">Hashicorp’s Consul</a> in a number of ways in the Unimog control plane (we have an independent Consul server cluster in each data center):</p><ul><li><p>Consul provides a key-value store, with support for blocking queries so that changes to values can be received promptly. We use this to propagate the forwarding tables and VIP address information from the conductor to xdpd on the servers.</p></li><li><p>Consul provides server- and service-level health checks. We use this as the source of health information for Unimog.</p></li><li><p>The conductor stores its state in the Consul KV store, and uses Consul’s distributed locks to ensure that only one conductor instance is active.</p></li></ul><p>The conductor obtains server load information from Prometheus, which we already use for metrics throughout our systems. It balances the load across the servers using a control loop, periodically adjusting the forwarding tables to send more connections to underloaded servers and less connections to overloaded servers. The load for a server is defined by a Prometheus metric expression which measures processor utilization (with some intricacies to better handle characteristics of our workloads). The determination of whether a server is underloaded or overloaded is based on comparison with the average value of the load metric, and the adjustments made to the forwarding table are proportional to the deviation from the average. So the result of the feedback loop is that the load metric for all servers converges on the average.</p><p>Finally, the conductor queries internal Cloudflare APIs to obtain the necessary information on servers and addresses.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/25doef0hn0sJTI8oGzgoOk/fb025967eab0ee87dcb9ead2724dc755/control.png" />
            
            </figure><p>Unimog is a critical system: incorrect, poorly adjusted or stale forwarding tables could cause incoming network traffic to a data center to be dropped, or servers to be overloaded, to the point that a data center would have to be removed from service. To maintain a high quality of service and minimise the overhead of managing our many edge data centers, we have to be able to upgrade all components. So to the greatest extent possible, all components are able to tolerate brief absences of the other components without any impact to service. In some cases this is possible through careful design. In other cases, it requires explicit handling. For example, we have found that Consul can temporarily report inaccurate health information for a server and its services when the Consul agent on that server is restarted (for example, in order to upgrade Consul). So we implemented the necessary logic in the conductor to detect and disregard these transient health changes.</p><p>Unimog also forms a complex system with feedback loops: The conductor reacts to its observations of behaviour of the servers, and the servers react to the control information they receive from the conductor. This can lead to behaviours of the overall system that are hard to anticipate or test for. For instance, not long after we deployed Unimog we encountered surprising behaviour when data centers became overloaded. This is of course a scenario that we strive to avoid, and we have automated systems to remove traffic from overloaded data centers if it does. But if a data center became sufficiently overloaded, then health information from its servers would indicate that many servers were degraded to the point that Unimog would stop sending new connections to those servers. Under normal circumstances, this is the correct reaction to a degraded server. But if enough servers become degraded, diverting new connections to other servers would mean those servers became degraded, while the original servers were able to recover. So it was possible for a data center that became temporarily overloaded to get stuck in a state where servers oscillated between healthy and degraded, even after the level of demand on the data center had returned to normal. To correct this issue, the conductor now has logic to distinguish between isolated degraded servers and such data center-wide problems. We have continued to improve Unimog in response to operational experience, ensuring that it behaves in a predictable manner over a wide range of conditions.</p>
    <div>
      <h2>UDP Support</h2>
      <a href="#udp-support">
        
      </a>
    </div>
    <p>So far, we have described Unimog’s support for directing TCP connections. But Unimog also supports UDP traffic. UDP does not have explicit connections between clients and servers, so how it works depends upon how the UDP application exchanges packets between the client and server. There are a few cases of interest:</p>
    <div>
      <h3>Request-response UDP applications</h3>
      <a href="#request-response-udp-applications">
        
      </a>
    </div>
    <p>Some applications, such as DNS, use a simple request-response pattern: the client sends a request packet to the server, and expects a response packet in return. Here, there is nothing corresponding to a connection (the client only sends a single packet, so there is no requirement to make sure that multiple packets arrive at the same server). But Unimog can still provide value by spreading the requests across our servers.</p><p>To cater to this case, Unimog operates as described in previous sections, hashing the 4-tuple from the packet headers (the source and destination IP addresses and ports). But the Beamer daisy-chaining technique that allows connections to be maintained does not apply here, and so the buckets in the forwarding table only have a single slot.</p>
    <div>
      <h3>UDP applications with flows</h3>
      <a href="#udp-applications-with-flows">
        
      </a>
    </div>
    <p>Some UDP applications have long-lived <i>flows</i> of packets between the client and server. Like TCP connections, these flows are identified by the 4-tuple. It is necessary that such flows go to the same server (even when Cloudflare is just passing a flow through to the origin server, it is convenient for detecting and mitigating certain kinds of attack to have that flow pass through a single server within one of Cloudflare’s data centers).</p><p>It's possible to treat these flows by hashing the 4-tuple, skipping the Beamer daisy-chaining technique as for request-response applications. But then adding servers will cause some flows to change servers (this would effectively be a form of consistent hashing). For UDP applications, we can’t say in general what impact this has, as we can for TCP connections. But it’s possible that it causes some disruption, so it would be nice to avoid this.</p><p>So Unimog adapts the daisy-chaining technique to apply it to UDP flows. The outline remains similar to that for TCP: the same redirector component on each server decides whether to send a packet on a second hop. But UDP does not have anything corresponding to TCP’s SYN packet that indicates a new connection. So for UDP, the part that depends on SYNs is removed, and the logic applied for each packet becomes:</p><ul><li><p>The redirector checks whether the packet belongs to a connection with a corresponding UDP socket on the first-hop server. If so, it is processed by that server.</p></li><li><p>Otherwise, the packet has no corresponding UDP socket on the first-hop server. So it is forwarded on to the second-hop server to be processed there (in the expectation that it belongs to some flow established on the second-hop server that we wish to maintain).</p></li></ul><p>Although the change compared to the TCP logic is not large, it has the effect of switching the roles of the first- and second-hop servers: For UDP, new flows go to the second-hop server. The Unimog control plane has to take account of this when it updates a forwarding table. When it introduces a server into a bucket, that server should receive new connections or flows. For a TCP trafficset, this means it becomes the first-hop server. For UDP trafficset, it must become the second-hop server.</p><p>This difference between handling of TCP and UDP also leads to higher overheads for UDP. In the case of TCP, as new connections are formed and old connections terminate over time, fewer packets will require the second hop, and so the overhead tends to diminish. But with UDP, new connections always involve the second hop. This is why we differentiate the two cases, taking advantage of SYN packets in the TCP case.</p><p>The UDP logic also places a requirement on services. The redirector must be able to match packets to the corresponding sockets on a server according to their 4-tuple. This is not a problem in the TCP case, because all TCP connections are represented by connected sockets in the BSD sockets API (these sockets are obtained from an accept system call, so that they have a local address and a peer address, determining the 4-tuple). But for UDP, unconnected sockets (lacking a declared peer address) can be used to send and receive packets. So some UDP services only use unconnected sockets. For the redirector logic above to work, services must create connected UDP sockets in order to expose their flows to the redirector.</p>
    <div>
      <h3>UDP applications with sessions</h3>
      <a href="#udp-applications-with-sessions">
        
      </a>
    </div>
    <p>Some UDP-based protocols have explicit <i>sessions</i>, with a session identifier in each packet. Session identifiers allow sessions to persist even if the 4-tuple changes. This happens in mobility scenarios - for example, if a mobile device passes from a WiFi to a cellular network, causing its IP address to change. An example of a UDP-based protocol with session identifiers is <a href="/the-road-to-quic/">QUIC</a> (which calls them connection IDs).</p><p>Our Unimog XDP program allows a <i>flow dissector</i> to be configured for different trafficsets. The flow dissector is the part of the code that is responsible for taking a packet and extracting the value that identifies the flow or connection (this value is then hashed and used for the lookup into the forwarding table). For TCP and UDP, there are default flow dissectors that extract the 4-tuple. But specialised flow dissectors can be added to handle UDP-based protocols.</p><p>We have used this functionality in our WARP product. We extended the Wireguard protocol used by WARP in a backwards-compatible way to include a session identifier, and added a flow dissector to Unimog to exploit it. There are more details in <a href="/warp-technical-challenges/">our post on the technical challenges of WARP</a>.</p>
    <div>
      <h2>Conclusion</h2>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>Unimog has been deployed to all of Cloudflare’s edge data centers for over a year, and it has become essential to our operations. Throughout that time, we have continued to enhance Unimog (many of the features described here were not present when it was first deployed). So the ease of developing and deploying changes, due to XDP and xdpd, has been a significant benefit. Today we continue to extend it, to support more services, and to help us manage our traffic and the load on our servers in more contexts.</p> ]]></content:encoded>
            <category><![CDATA[Deep Dive]]></category>
            <category><![CDATA[Load Balancing]]></category>
            <category><![CDATA[Edge]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <category><![CDATA[Product News]]></category>
            <guid isPermaLink="false">7wYPcBFe2ItK009d2hZ5FS</guid>
            <dc:creator>David Wragg</dc:creator>
        </item>
        <item>
            <title><![CDATA[Why is there a "V" in SIGSEGV Segmentation Fault?]]></title>
            <link>https://blog.cloudflare.com/why-is-there-a-v-in-sigsegv-segmentation-fault/</link>
            <pubDate>Thu, 18 Jun 2020 11:56:33 GMT</pubDate>
            <description><![CDATA[ My program received a SIGSEGV signal and crashed with "Segmentation Fault" message. Where does the "V" come from? 

Did I read it wrong? Was there a "Segmentation *V*ault?"? Or did Linux authors make a mistake? Shouldn't the signal be named SIGSEGF? 
 ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Another long night. I was working on my perfect, bug-free program in C, when the predictable thing happened:</p>
            <pre><code>$ clang skynet.c -o skynet
$ ./skynet.out 
Segmentation fault (core dumped)</code></pre>
            <p>Oh, well... Maybe I'll be more lucky taking over the world another night. But then it struck me. My program received a SIGSEGV signal and crashed with "Segmentation Fault" message. Where does the "V" come from?</p><p>Did I read it wrong? Was there a "Segmentation _V_ault?"? Or did Linux authors make a mistake? Shouldn't the signal be named SIGSEGF?</p><p>I asked my colleagues and <a href="https://twitter.com/dwragg">David Wragg</a> quickly told me that the signal name stands for "Segmentation Violation". I guess that makes sense. Long long time ago, computers used to have <a href="https://en.wikipedia.org/wiki/X86_memory_segmentation">memory segmentation</a>. Each memory segment had defined length - called Segment Limit. Accessing data over this limit caused a processor fault. This error code got re-used <a href="https://en.wikipedia.org/wiki/Memory_segmentation#Segmentation_with_paging">by newer systems that used paging</a>. I think the Intel manuals call this error <a href="https://en.wikipedia.org/wiki/Page_fault#Invalid">"Invalid Page Fault"</a>. When it's triggered it gets reported to the userspace as a SIGSEGV signal. End of story.</p><p>Or is it?</p><p><a href="https://twitter.com/mahtin">Martin Levy</a> pointed me to an ancient <a href="http://man.cat-v.org/unix-6th/2/signal">Version 6th UNIX documentation on "signal"</a>. This is from around 1978:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4guHgMnVrZOyzBS0j2vOF3/1794a378bb035a8f938e3c11fb65de19/IMG_5177.jpg" />
            
            </figure><p>Look carefully. There is no SIGSEGV signal! Signal number 11 is called SIGSEG!</p><p>It seems that userspace parts of the UNIX tree (i.e. /usr/include/signal.h) switched to SIGSEGV fairly early on. But the kernel internals continued to use the name SIGSEG for much longer.</p><p>Looking deeper David found that PDP11 trap vector used wording <a href="https://github.com/dspinellis/unix-history-repo/blob/Research-V4-Snapshot-Development/sys/ken/low.s#L59">"segmentation violation"</a>. This shows up in Research V4 Edition in the UNIX history repo, but it doesn't mean it was introduced in V4 - it's just because V4 is the first version with code still available.</p><p>This trap was converted into <a href="https://github.com/dspinellis/unix-history-repo/blob/Research-V4-Snapshot-Development/sys/ken/trap.c#L73">SIGSEG signal in trap.c</a> file.</p><p>The file /usr/include/signal.h appears in the tree for Research V7, <a href="https://github.com/dspinellis/unix-history-repo/blob/Research-V7-Snapshot-Development/usr/include/signal.h">with the name SIGSEGV</a>. But the kernel <a href="https://github.com/dspinellis/unix-history-repo/blob/Research-V7-Snapshot-Development/usr/sys/sys/trap.c#L177">still called it SIGSEG at the time</a></p><p>It seems the kernel side was <a href="https://github.com/dspinellis/unix-history-repo/blob/BSD-4/usr/src/sys/sys/trap.c#L67">renamed to SIGSEGV in BSD-4</a>.</p><p>Here you go. Originally the signal was called SIGSEG. It was subsequently renamed SIGSEGV in the userspace and a bit later - around 1980 - to SIGSEGV on the kernel side. Apparently there are still no Segmentation Vaults found on UNIX systems.</p><p>As for my original crash, I fixed it - of course - by catching the signal and jumping over the offending instruction. On Linux it is totally possible to catch and handle SIGSEGV. With that fix, my code will never again crash. For sure.</p>
            <pre><code>#define _GNU_SOURCE
#include &lt;signal.h&gt;
#include &lt;stdio.h&gt;
#include &lt;ucontext.h&gt;

static void sighandler(int signo, siginfo_t *si, void* v_context)
{
    ucontext_t *context = v_context;
    context-&gt;uc_mcontext.gregs[REG_RIP] += 10;
}

int *totally_null_pointer = NULL;

int main() {
    struct sigaction psa;
    psa.sa_sigaction = sighandler;
    sigaction(SIGSEGV, &amp;psa, NULL);

    printf("Before NULL pointer dereference\n");
    *totally_null_pointer = 1;
    __asm__ __volatile__("nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;");
    printf("After NULL pointer. Still here!\n");

    return 0;
}</code></pre>
             ]]></content:encoded>
            <category><![CDATA[Deep Dive]]></category>
            <category><![CDATA[Developers]]></category>
            <guid isPermaLink="false">4vXHuB3JMI6WziJEJtc90H</guid>
            <dc:creator>Marek Majkowski</dc:creator>
            <dc:creator>David Wragg</dc:creator>
        </item>
        <item>
            <title><![CDATA[However improbable: The story of a processor bug]]></title>
            <link>https://blog.cloudflare.com/however-improbable-the-story-of-a-processor-bug/</link>
            <pubDate>Thu, 18 Jan 2018 12:06:48 GMT</pubDate>
            <description><![CDATA[ Processor problems have been in the news lately, due to the Meltdown and Spectre vulnerabilities. But generally, engineers writing software assume that computer hardware operates in a reliable, well-understood fashion, and that any problems lie on the software side of the software-hardware divide. ]]></description>
            <content:encoded><![CDATA[ <p>Processor problems have been in the news lately, due to the <a href="/meltdown-spectre-non-technical/">Meltdown and Spectre vulnerabilities</a>. But generally, engineers writing software assume that computer hardware operates in a reliable, well-understood fashion, and that any problems lie on the software side of the software-hardware divide. Modern processor chips routinely execute many billions of instructions in a second, so any erratic behaviour must be very hard to trigger, or it would quickly become obvious.</p><p>But sometimes that assumption of reliable processor hardware doesn’t hold. Last year at Cloudflare, we were affected by a bug in one of Intel’s processor models. Here’s the story of how we found we had a mysterious problem, and how we tracked down the cause.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/259H08ZlP34cvwO953wEbe/9dfefb01771e3aff180bde9186c3c319/Sherlock_holmes_pipe_hat-1.jpg" />
            
            </figure><p><a href="http://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA-3.0</a> <a href="https://commons.wikimedia.org/wiki/File:Sherlock_holmes_pipe_hat.jpg">image</a> by Alterego</p>
    <div>
      <h3>Prologue</h3>
      <a href="#prologue">
        
      </a>
    </div>
    <p>Back in February 2017, Cloudflare <a href="/incident-report-on-memory-leak-caused-by-cloudflare-parser-bug/">disclosed a security problem</a> which became known as Cloudbleed. The bug behind that incident lay in some code that ran on our servers to parse HTML. In certain cases involving invalid HTML, the parser would read data from a region of memory beyond the end of the buffer being parsed. The adjacent memory might contain other customers’ data, which would then be returned in the HTTP response, and the result was Cloudbleed.</p><p>But that wasn’t the only consequence of the bug. Sometimes it could lead to an invalid memory read, causing the NGINX process to crash, and we had metrics showing these crashes in the weeks leading up to the discovery of Cloudbleed. So one of the measures we took to prevent such a problem happening again was to require that every crash be investigated in detail.</p><p>We acted very swiftly to address Cloudbleed, and so ended the crashes due to that bug, but that did not stop all crashes. We set to work investigating these other crashes.</p>
    <div>
      <h3>Crash is not a technical term</h3>
      <a href="#crash-is-not-a-technical-term">
        
      </a>
    </div>
    <p>But what exactly does “crash” mean in this context? When a processor detects an attempt to access invalid memory (more precisely, an address without a valid page in the page tables), it signals a page fault to the operating system’s kernel. In the case of Linux, these page faults result in the delivery of a SIGSEGV signal to the relevant process (the name SIGSEGV derives from the historical Unix term “segmentation violation”, also known as a segmentation fault or segfault). The default behaviour for SIGSEGV is to terminate the process. It’s this abrupt termination that was one symptom of the Cloudbleed bug.</p><p>This possibility of invalid memory access and the resulting termination is mostly relevant to processes written in C or C++. Higher-level compiled languages, such as Go and JVM-based languages, use type systems which prevent the kind of low-level programming errors that can lead to accesses of invalid memory. Furthermore, such languages have sophisticated runtimes that take advantage of <a href="https://pdos.csail.mit.edu/6.828/2017/readings/appel-li.pdf">page faults for implementation tricks that make them more efficient</a> (a process can install a signal handler for SIGSEGV so that it does not get terminated, and instead can recover from the situation). And for interpreted languages such as Python, the interpreter checks that conditions leading to invalid memory accesses cannot occur. So unhandled SIGSEGV signals tend to be restricted to programming in C and C++.</p><p>SIGSEGV is not the only signal that indicates an error in a process and causes termination. We also saw process terminations due to SIGABRT and SIGILL, suggesting other kinds of bugs in our code.</p><p>If the only information we had about these terminated NGINX processes was the signal involved, investigating the causes would have been difficult. But there is another feature of Linux (and other Unix-derived operating systems) that provided a path forward: core dumps. A core dump is a file written by the operating system when a process is terminated abruptly. It records the full state of the process at the time it was terminated, allowing post-mortem debugging. The state recorded includes:</p><ul><li><p>The processor register values for all threads in the process (the values of some program variables will be held in registers)</p></li><li><p>The contents of the process’ conventional memory regions (giving the values of other program variables and heap data)</p></li><li><p>Descriptions of regions of memory that are read-only mappings of files, such as executables and shared libraries</p></li><li><p>Information associated with the signal that caused termination, such as the address of an attempted memory access that led to a SIGSEGV</p></li></ul><p>Because core dumps record all this state, their size depends upon the program involved, but they can be fairly large. Our NGINX core dumps are often several gigabytes.</p><p>Once a core dump has been recorded, it can be inspected using a debugging tool such as gdb. This allows the state from the core dump to be explored in terms of the original program source code, so that you can inquire about the program stack and contents of variables and the heap in a reasonably convenient manner.</p><p>A brief aside: Why are core dumps called core dumps? It’s a historical term that originated in the 1960s when the principal form of random access memory was <a href="https://en.wikipedia.org/wiki/Magnetic-core_memory">magnetic core memory</a>. At the time, the word core was used as a shorthand for memory, so “core dump” means a dump of the contents of memory.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4DW66RVAAUllbxyW26n4oP/9fe72dacf0f67f9a8a79dd4c115ba62a/coremem-2.jpg" />
            
            </figure><p><a href="http://creativecommons.org/licenses/by-sa/3.0/">CC BY-SA 3.0</a> <a href="https://en.wikipedia.org/wiki/Magnetic-core_memory#/media/File:KL_CoreMemory.jpg">image</a> by Konstantin Lanzet</p>
    <div>
      <h3>The game is afoot</h3>
      <a href="#the-game-is-afoot">
        
      </a>
    </div>
    <p>As we examined the core dumps, we were able to track some of them back to more bugs in our code. None of them leaked data as Cloudbleed had, or had other security implications for our customers. Some might have allowed an attacker to try to impact our service, but the core dumps suggested that the bugs were being triggered under innocuous conditions rather than attacks. We didn’t have to fix many such bugs before the number of core dumps being produced had dropped significantly.</p><p>But there were still some core dumps being produced on our servers — about one a day across our whole fleet of servers. And finding the root cause of these remaining ones proved more difficult.</p><p>We gradually began to suspect that these residual core dumps were not due to bugs in our code. These suspicions arose because we found cases where the state recorded in the core dump did not seem to be possible based on the program code (and in examining these cases, we didn’t rely on the C code, but looked at the machine code produced by the compiler, in case we were dealing with compiler bugs). At first, as we discussed these core dumps among the engineers at Cloudflare, there was some healthy scepticism about the idea that the cause might lie outside of our code, and there was at least one joke about <a href="https://www.computerworld.com/article/3171677/it-industry/computer-crash-may-be-due-to-forces-beyond-our-solar-system.html">cosmic rays</a>. But as we amassed more and more examples, it became clear that something unusual was going on. Finding yet another “mystery core dump”, as we had taken to calling them, became routine, although the details of these core dumps were diverse, and the code triggering them was spread throughout our code base. The common feature was their apparent impossibility.</p><p>There was no obvious pattern to the servers which produced these mystery core dumps. We were getting about one a day on average across our fleet of servers. So the sample size was not very big, but they seemed to be evenly spread across all our servers and datacenters, and no one server was struck twice. The probability that an individual server would get a mystery core dump seemed to be very low (about one per ten years of server uptime, assuming they were indeed equally likely for all our servers). But because of our large number of servers, we got a steady trickle.</p>
    <div>
      <h3>In quest of a solution</h3>
      <a href="#in-quest-of-a-solution">
        
      </a>
    </div>
    <p>The rate of mystery core dumps was low enough that it didn’t appreciably impact the service to our customers. But we were still committed to examining every core dump that occurred. Although we got better at recognizing these mystery core dumps, investigating and classifying them was a drain on engineering resources. We wanted to find the root cause and fix it. So we started to consider causes that seemed somewhat plausible:</p><p>We looked at hardware problems. Memory errors in particular are a real possibility. But our servers use ECC (Error-Correcting Code) memory which can detect, and in most cases correct, any memory errors that do occur. Furthermore, any memory errors should be recorded in the IPMI logs of the servers. We do see some memory errors on our server fleet, but they were not correlated with the core dumps.</p><p>If not memory errors, then could there be a problem with the processor hardware? We mostly use Intel Xeon processors, of various models. These have a good reputation for reliability, and while the rate of core dumps was low, it seemed like it might be too high to be attributed to processor errors. We searched for reports of similar issues, and asked on the grapevine, but didn’t hear about anything that seemed to match our issue.</p><p>While we were investigating, an <a href="https://lists.debian.org/debian-devel/2017/06/msg00308.html">issue with Intel Skylake processors</a> came to light. But at that time we did not have Skylake-based servers in production, and furthermore that issue related to particular code patterns that were not a common feature of our mystery core dumps.</p><p>Maybe the core dumps were being incorrectly recorded by the Linux kernel, so that a mundane crash due to a bug in our code ended up looking mysterious? But we didn’t see any patterns in the core dumps that pointed to something like this. Also, upon an unhandled SIGSEGV, the kernel generates a log line with a small amount of information about the cause, like this:</p>
            <pre><code>segfault at ffffffff810c644a ip 00005600af22884a sp 00007ffd771b9550 error 15 in nginx-fl[5600aeed2000+e09000]</code></pre>
            <p>We checked these log lines against the core dumps, and they were always consistent.</p><p>The kernel has a role in controlling the processor’s <a href="https://en.wikipedia.org/wiki/Memory_management_unit">Memory Management Unit</a> to provide virtual memory to application programs. So kernel bugs in that area can lead to surprising results (and we have encountered such a bug at Cloudflare in a different context). But we examined the kernel code, and searched for reports of relevant bugs against Linux, without finding anything.</p><p>For several weeks, our efforts to find the cause were not fruitful. Due to the very low frequency of the mystery core dumps when considered on a per-server basis, we couldn’t follow the usual last-resort approach to problem solving - changing various possible causative factors in the hope that they make the problem more or less likely to occur. We needed another lead.</p>
    <div>
      <h3>The solution</h3>
      <a href="#the-solution">
        
      </a>
    </div>
    <p>But eventually, we noticed something crucial that we had missed until that point: all of the mystery core dumps came from servers containing <a href="https://ark.intel.com/products/91767/Intel-Xeon-Processor-E5-2650-v4-30M-Cache-2_20-GHz">The Intel Xeon E5-2650 v4</a>. This model belongs to the generation of Intel processors that had the codename “Broadwell”, and it’s the only model of that generation that we use in our edge servers, so we simply call these servers Broadwells. The Broadwells made up about a third of our fleet at that time, and they were in many of our datacenters. This explains why the pattern was not immediately obvious.</p><p>This insight immediately threw the focus of our investigation back onto the possibility of processor hardware issues. We downloaded <a href="https://www.intel.co.uk/content/www/uk/en/processors/xeon/xeon-e5-v4-spec-update.html">Intel’s Specification Update</a> for this model. In these Specification Update documents Intel discloses all the ways that its processors deviate from their published specifications, whether due to benign discrepancies or bugs in the hardware (Intel entertainingly calls these “errata”).</p><p>The Specification Update described 85 issues, most of which are obscure issues of interest mainly to the developers of the BIOS and operating systems. But one caught our eye: “BDF76 An Intel® Hyper-Threading Technology Enabled Processor May Exhibit Internal Parity Errors or Unpredictable System Behavior”. The symptoms described for this issue are very broad (“unpredictable system behavior may occur”), but what we were observing seemed to match the description of this issue better than any other.</p><p>Furthermore, the Specification Update stated that BDF76 was fixed in a microcode update. Microcode is firmware that controls the lowest-level operation of the processor, and can be updated by the BIOS (from system vendor) or the OS. Microcode updates can change the behaviour of the processor to some extent (exactly how much is a closely-guarded secret of Intel, although <a href="https://software.intel.com/sites/default/files/managed/c5/63/336996-Speculative-Execution-Side-Channel-Mitigations.pdf">the recent microcode updates to address the Spectre vulnerability</a> give some idea of the impressive degree to which Intel can reconfigure the processor’s behaviour).</p><p>The most convenient way for us to apply the microcode update to our Broadwell servers at that time was via a BIOS update from the server vendor. But rolling out a BIOS update to so many servers in so many data centers takes some planning and time to conduct. Due to the low rate of mystery core dumps, we would not know if BDF76 was really the root cause of our problems until a significant fraction of our Broadwell servers had been updated. A couple of weeks of keen anticipation followed while we awaited the outcome.</p><p>To our great relief, once the update was completed, the mystery core dumps stopped. This chart shows the number of core dumps we were getting each day for the relevant months of 2017:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3swQhL1euB2VpWjtlom4Yh/0be3eebcb41da11502e23af45ce08eb7/coredumps.png" />
            
            </figure><p>As you can see, after the microcode update there is a marked reduction in the rate of core dumps. But we still get some core dumps. These are not mysteries, but represent conventional issues in our software. We continue to investigate and fix them to ensure they don’t represent security issues in our service.</p>
    <div>
      <h3>The conclusion</h3>
      <a href="#the-conclusion">
        
      </a>
    </div>
    <p>Eliminating the mystery core dumps has made it easier to focus on any remaining crashes that are due to our code. It removes the temptation to dismiss a core dump because its cause is obscure.</p><p>And for some of the core dumps that we see now, understanding the cause can be very challenging. They correspond to very unlikely conditions, and often involve a root cause that is distant from the immediate issue that triggered the core dump. For example, we see segfaults in LuaJIT (which we embed in NGINX via OpenResty) that are not due to problems in LuaJIT, but rather because LuaJIT is particularly susceptible to damage to its data structures by bugs in unrelated C code.</p><p><i>Excited by core dump detective work? Or building systems at a scale where once-in-a-decade problems can get triggered every day? Then </i><a href="https://www.cloudflare.com/careers/"><i>join our team</i></a><i>.</i></p> ]]></content:encoded>
            <category><![CDATA[Reliability]]></category>
            <category><![CDATA[Bugs]]></category>
            <category><![CDATA[Vulnerabilities]]></category>
            <category><![CDATA[NGINX]]></category>
            <guid isPermaLink="false">39XoGxaU2MKp2cinJiQEXb</guid>
            <dc:creator>David Wragg</dc:creator>
        </item>
        <item>
            <title><![CDATA[A Very WebP New Year from Cloudflare]]></title>
            <link>https://blog.cloudflare.com/a-very-webp-new-year-from-cloudflare/</link>
            <pubDate>Wed, 21 Dec 2016 14:00:00 GMT</pubDate>
            <description><![CDATA[ Cloudflare has an automatic image optimization feature called Polish, available for paid plan users. It recompresses images and stripping excess data, speeding up delivery to browsers. ]]></description>
            <content:encoded><![CDATA[ <p>Cloudflare has an automatic image optimization feature called <a href="/introducing-polish-automatic-image-optimizati/">Polish</a>, available to customers on paid plans. It recompresses images and removes unnecessary data so that they are delivered to browsers more quickly.</p><p>Up until now, Polish has not changed image types when optimizing (even if, for example, a PNG might sometimes have been smaller than the equivalent JPEG). But a new feature in Polish allows us to swap out an image for an equivalent image compressed using Google’s WebP format when the browser is capable of handling WebP and delivering that type of image would be quicker.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6RCs2VzrEL7pYO3RNWBRYa/5434d04bd47f702aa548ea48985c2aff/holly.jpg" />
            
            </figure><p><a href="https://creativecommons.org/licenses/by/2.0/">CC-BY 2.0</a> <a href="https://www.flickr.com/photos/john47kent/5307525503/">image</a> by <a href="https://www.flickr.com/photos/john47kent/">John Stratford</a></p>
    <div>
      <h3>What is WebP?</h3>
      <a href="#what-is-webp">
        
      </a>
    </div>
    <p>The main image formats used on the web haven’t changed much since the early days (apart from the SVG vector format, PNG was the last one to establish itself, <a href="https://en.wikipedia.org/wiki/Portable_Network_Graphics#History_and_development">almost two decades ago</a>).</p><p><a href="https://en.wikipedia.org/wiki/WebP">WebP</a> is a newer image format for the web, proposed by Google. It takes advantage of progress in <a href="https://www.cloudflare.com/learning/performance/glossary/what-is-image-compression/">image compression techniques</a> since formats such as JPEG and PNG were designed. It is often able to compress the images into a significantly smaller amount of data than the older formats.</p><p>WebP is versatile and able to replace the three main raster image formats used on the web today:</p><ul><li><p>WebP can do lossy compression, so it can be used instead of JPEG for photographic and photo-like images.</p></li><li><p>WebP can do lossless compression, and supports an alpha channel meaning images can have transparent regions. So it can be used instead of PNG, such as for images with sharp transitions that should be reproduced exactly (e.g. line art and graphic design elements).</p></li><li><p>WebP images can be animated, so it can be used as a replacement for animated GIF images.</p></li></ul><p>Currently, the main browser that supports WebP is Google’s Chrome (both on desktop and mobile devices). See <a href="http://caniuse.com/#feat=webp">the WebP page on caniuse.com</a> for more details.</p>
    <div>
      <h3>Polish WebP conversion</h3>
      <a href="#polish-webp-conversion">
        
      </a>
    </div>
    <p>Customers on the Pro, Business, and Enterprise plans can enable the automatic creation of WebP images by checking the WebP box in the Polish settings for a zone (these are found on the “Speed” page of the dashboard):</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5Beloplqsl1SNVyjFSOyl4/d6853afc514b5a6fc9cf94758d629695/polish-webp.png" />
            
            </figure><p>When this is enabled, Polish will optimize images just as it always has. But it will also convert the image to WebP, if WebP can shrink the image data more than the original format. These WebP images are only returned to web browsers that indicate they support WebP (e.g. Google Chrome), so most websites using Polish should be able to benefit from WebP conversion.</p><p>(Although Polish can now produce WebP images by converting them from other formats, it can't consume WebP images to optimize them. If you put a WebP image on an origin site, Polish won't do anything with it. Until the WebP ecosystem grows and matures, it is uncertain that attempting to optimize WebP is worthwhile.)</p><p>Polish has two modes: <i>lossless</i> and <i>lossy</i>. In lossless mode, JPEG images are optimized to remove unnecessary data, but the image displayed is unchanged. In lossy mode, Polish reduces the quality of JPEG images in a way that should not have a significant visible effect, but allows it to further reduce the size of the image data.</p><p>These modes are respected when JPEG images are converted to WebP. In lossless mode, the conversion is done in a way that preserves the image as faithfully as possible (due to the nature of the conversion, the resulting WebP might not be exactly identical, but there are unlikely to be any visible differences). In lossy mode, the conversion sacrifices a little quality in order to shrink the image data further, but as before, there should not be a significant visible effect.</p><p>These modes do not affect PNGs and GIFs, as these are lossless formats and so Polish will preserve images in those formats exactly.</p><p>Note that WebP conversion does not change the URLs of images, even if the file extension in the URL implies a different format. For example, a JPEG image at <code>https://example.com/picture.jpg</code> that has been converted to WebP will still have that same URL. The “Content-Type” HTTP header tells the browser the true format of an image.</p>
    <div>
      <h3>By the Numbers</h3>
      <a href="#by-the-numbers">
        
      </a>
    </div>
    <p>A few studies have been published of how well WebP compresses images compared with established formats. These studies provide a useful picture of how WebP performs. But before we released our WebP support, we decided to do a survey based on the context on which we planned to use WebP:</p><ul><li><p>We evaluated WebP based on a collection of images gathered from the websites of our customers. The corpus consisted of 23,500 images (JPEG, PNG and GIFs).</p></li><li><p>Some studies compare WebP with JPEG by taking uncompressed images and compressing them to JPEG and WebP directly. But we wanted to know what happens when we convert an image that was has already been compressed as a JPEG. In a sense this is an unfair test, because a JPEG may contain artifacts due to compression that would not be present in the original raw image, and conversion to WebP may try to retain those artifacts. But it is such conversions matter for our use of WebP (this consideration does not apply to PNG and GIF conversions, because they are lossless).</p></li><li><p>We’re not just interested in whether WebP conversion can shrink images found on the web. We want to know how much WebP allows Polish to reduce the size further than it already does, thus providing a real end-user benefit. So our survey also includes the results of Polish without WebP.</p></li><li><p>In some cases, converting to WebP does not produce a result smaller than the optimized image in the original format. In such cases, we discard the WebP image. So the figures presented below do not penalize WebP for such cases.</p></li></ul><p>Here is a chart showing the results of Polish, with and without WebP conversion. For each format, the average original image size is normalized to 100%, and the average sizes after Polishing are shown relative to that.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5YazeQmjUBmLEir39hYrUh/bab2f4768bf57f5a1f8aecd3f07d9d9a/webp-chart.png" />
            
            </figure><p>Here are the average savings corresponding to the chart:</p><table><tr><td><p><b>Original Format</b></p></td><td><p><b>Polish without WebP</b></p></td><td><p><b>Polish using WebP</b></p></td></tr><tr><td><p>JPEG (with Polish lossless mode)</p></td><td><p>9%</p></td><td><p>19%</p></td></tr><tr><td><p>JPEG (with Polish lossy mode)</p></td><td><p>34%</p></td><td><p>47%</p></td></tr><tr><td><p>PNG</p></td><td><p>16%</p></td><td><p>38%</p></td></tr><tr><td><p>GIF</p></td><td><p>3%</p></td><td><p>16%</p></td></tr></table><p>(The saving is calculated as 100% - (polished size) / (original size).)</p><p>As you can see, WebP conversion achieves significant size improvements not only for JPEG images, but also for PNG and GIF images. We believe supporting WebP will result in lower bandwidth and faster website delivery.</p>
    <div>
      <h3>… and a WebP New Year</h3>
      <a href="#and-a-webp-new-year">
        
      </a>
    </div>
    <p>WebP does not yet have the same level of browser support as JPEG, PNG and GIF, but we are excited about its potential to streamline web pages. Polish WebP conversion allows our customers to adopt WebP with a simple change to the settings in the Cloudflare dashboard. So, if you are on one of our <a href="https://www.cloudflare.com/plans/">paid plans</a>, we encourage you to try it out today.</p><p>PS — Want to help optimize the web? We’re <a href="https://www.cloudflare.com/join-our-team/">hiring</a>.</p> ]]></content:encoded>
            <category><![CDATA[Cloudflare Polish]]></category>
            <category><![CDATA[Compression]]></category>
            <category><![CDATA[Optimization]]></category>
            <category><![CDATA[WebP]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <guid isPermaLink="false">7M6kU8b93kMUiHWhgQTkjv</guid>
            <dc:creator>David Wragg</dc:creator>
        </item>
    </channel>
</rss>