<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>bpftool Archives - Linuxcent</title>
	<atom:link href="https://linuxcent.com/tag/bpftool/feed/" rel="self" type="application/rss+xml" />
	<link>https://linuxcent.com/tag/bpftool/</link>
	<description>Infrastructure security, from the kernel up.</description>
	<lastBuildDate>Tue, 07 Jul 2026 03:13:25 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0.4</generator>

<image>
	<url>https://linuxcent.com/wp-content/uploads/2026/04/favicon-512x512-1-150x150.png</url>
	<title>bpftool Archives - Linuxcent</title>
	<link>https://linuxcent.com/tag/bpftool/</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">211632295</site>	<item>
		<title>The Audit Playbook — Four Commands to See Any Cluster</title>
		<link>https://linuxcent.com/the-audit-playbook-four-commands-to-see-any-cluster/</link>
					<comments>https://linuxcent.com/the-audit-playbook-four-commands-to-see-any-cluster/#respond</comments>
		
		<dc:creator><![CDATA[Vamshi Krishna Santhapuri]]></dc:creator>
		<pubDate>Tue, 14 Jul 2026 02:00:00 +0000</pubDate>
				<category><![CDATA[eBPF]]></category>
		<category><![CDATA[Audit]]></category>
		<category><![CDATA[bpftool]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[SRE]]></category>
		<guid isPermaLink="false">https://linuxcent.com/?p=2228</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 8</span> <span class="rt-label rt-postfix">minutes</span></span>eBPF: From Kernel to Cloud, Episode 14 What Is eBPF? · The BPF Verifier · eBPF vs Kernel Modules · eBPF Program Types · eBPF Maps · CO-RE and libbpf · XDP · TC eBPF · bpftrace · Network Flow Observability · DNS Observability · LSM and Tetragon · Process Lineage · The Audit Playbook ... <a title="The Audit Playbook — Four Commands to See Any Cluster" class="read-more" href="https://linuxcent.com/the-audit-playbook-four-commands-to-see-any-cluster/" aria-label="Read more about The Audit Playbook — Four Commands to See Any Cluster">Read more</a></p>
<p>The post <a href="https://linuxcent.com/the-audit-playbook-four-commands-to-see-any-cluster/">The Audit Playbook — Four Commands to See Any Cluster</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 8</span> <span class="rt-label rt-postfix">minutes</span></span><style>
pre{position:relative;background:#1e1e1e;color:#d4d4d4;
    padding:16px 16px 16px 20px;border-radius:6px;overflow-x:auto;
    font-family:'JetBrains Mono','Fira Code','Cascadia Code',Consolas,'Courier New',monospace;
    font-size:.88em;line-height:1.6;border-left:4px solid #555}
code{background:#f4f4f4;padding:2px 5px;border-radius:3px;font-size:.9em}
pre code{background:transparent;padding:0;color:inherit}
pre[data-lang="bash"],pre[data-lang="sh"],
pre[data-lang="shell"],pre[data-lang="zsh"]{border-left-color:#4ec9b0}
pre[data-lang="yaml"],pre[data-lang="json"],
pre[data-lang="toml"],pre[data-lang="xml"]{border-left-color:#569cd6}
pre[data-lang="python"],pre[data-lang="go"],pre[data-lang="rust"],
pre[data-lang="java"],pre[data-lang="c"],pre[data-lang="cpp"]{border-left-color:#c586c0}
pre[data-lang="text"],pre[data-lang="output"],
pre[data-lang="console"]{border-left-color:#888}
.lc-copy-btn{position:absolute;top:8px;right:8px;background:#2d2d2d;color:#ccc;
    border:1px solid #444;border-radius:4px;padding:3px 9px;font-size:.75em;
    font-family:system-ui,sans-serif;cursor:pointer;opacity:0;
    transition:opacity .15s,background .15s;line-height:1.6}
pre:hover .lc-copy-btn{opacity:1}
.lc-copy-btn:hover{background:#3a3a3a;color:#fff}
.lc-copy-btn.copied{color:#4ec9b0;border-color:#4ec9b0}
.lc-lang-badge{position:absolute;top:8px;left:20px;font-family:system-ui,sans-serif;
    font-size:.7em;color:#666;text-transform:uppercase;letter-spacing:.04em;
    line-height:1;pointer-events:none;opacity:0;transition:opacity .15s}
pre:hover .lc-lang-badge{opacity:1}
table{border-collapse:collapse;width:100%;margin:16px 0}
th,td{border:1px solid #ddd;padding:10px 14px;text-align:left}
th{background:#f0f0f0;font-weight:600}
tr:nth-child(even){background:#fafafa}
</style>
<p><script>
(function(){
  if(window.__lcCodeEnhanced)return;
  window.__lcCodeEnhanced=true;
  function enhance(){
    document.querySelectorAll('pre').forEach(function(pre){
      var code=pre.querySelector('code');
      var lang='';
      if(code){var m=(code.className||'').match(/language-(\S+)/);if(m)lang=m[1].toLowerCase();}
      if(lang)pre.setAttribute('data-lang',lang);
      if(lang){var badge=document.createElement('span');badge.className='lc-lang-badge';badge.textContent=lang;pre.insertBefore(badge,pre.firstChild);}
      var btn=document.createElement('button');
      btn.className='lc-copy-btn';btn.textContent='Copy';btn.setAttribute('aria-label','Copy code to clipboard');
      pre.appendChild(btn);
      btn.addEventListener('click',function(){
        var text=code?code.innerText:pre.innerText;
        if(navigator.clipboard&&window.isSecureContext){
          navigator.clipboard.writeText(text).then(function(){ok(btn);}).catch(function(){fb(text,btn);});
        }else{fb(text,btn);}
      });
    });
  }
  function ok(btn){btn.textContent='Copied!';btn.classList.add('copied');setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},2000);}
  function fb(text,btn){
    try{var ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;left:-9999px;top:-9999px;opacity:0';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);ok(btn);}
    catch(e){btn.textContent='✗ Failed';setTimeout(function(){btn.textContent='Copy';},2000);}
  }
  if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',enhance);}else{enhance();}
})();
</script></p>
<p><em>eBPF: From Kernel to Cloud, Episode 14</em><br />
<a href="/what-is-ebpf-linux-kubernetes/">What Is eBPF?</a> · <a href="/bpf-verifier-kubernetes-safety/">The BPF Verifier</a> · <a href="/ebpf-vs-kernel-modules-kubernetes/">eBPF vs Kernel Modules</a> · <a href="/ebpf-program-types-kubernetes/">eBPF Program Types</a> · <a href="/ebpf-maps-explained/">eBPF Maps</a> · <a href="/ebpf-co-re-libbpf-portable-programs/">CO-RE and libbpf</a> · <a href="/ebpf-xdp-kubernetes-networking/">XDP</a> · <a href="/tc-ebpf-kubernetes-network-policy/">TC eBPF</a> · <a href="/bpftrace-kernel-observability/">bpftrace</a> · <a href="/ebpf-network-flow-observability/">Network Flow Observability</a> · <a href="/ebpf-dns-observability-kubernetes/">DNS Observability</a> · <a href="/ebpf-lsm-tetragon-runtime-security/">LSM and Tetragon</a> · <a href="/ebpf-process-lineage-incident-response/">Process Lineage</a> · <strong>The Audit Playbook</strong></p>
<hr />
<h2 id="tldr">TL;DR</h2>
<ul>
<li>You can audit eBPF programs on any Kubernetes cluster with four <code class="" data-line="">bpftool</code> commands, regardless of which vendor&#8217;s tool loaded them — <code class="" data-line="">prog show</code>, <code class="" data-line="">map show</code>, <code class="" data-line="">net show</code> (plus <code class="" data-line="">cgroup tree</code>), and <code class="" data-line="">prog dump xlated</code><br />
  <em>(bpftool = the kernel-shipped CLI for inspecting loaded eBPF programs and maps directly, independent of any userspace agent or vendor tooling)</em></li>
<li><code class="" data-line="">bpftool prog show</code> gives you the inventory: every loaded program, its type, and — via its pinned path — usually which tool owns it</li>
<li><code class="" data-line="">bpftool map show</code> gives you the state: what data each program is reading or writing, cross-referenced by the <code class="" data-line="">map_ids</code> from the first command</li>
<li><code class="" data-line="">bpftool net show</code> and <code class="" data-line="">bpftool cgroup tree</code> give you the attachment points: which interface, which qdisc, which cgroup hook — where enforcement actually happens</li>
<li><code class="" data-line="">bpftool prog dump xlated</code> gives you the behavior: what the program does at the instruction level, for the cases where the pinned path doesn&#8217;t tell you enough</li>
<li>This sequence works whether the cluster is running Cilium, Falco, Tetragon, a hand-rolled XDP filter, or something with no documentation at all — the kernel doesn&#8217;t care who loaded the program</li>
</ul>
<hr />
<p>You inherit a cluster with no runbook, no README, and no answer to &#8220;what&#8217;s making the policy decisions.&#8221; Something on these nodes is dropping packets, or blocking execs, or both — and you have about ten minutes before the incident call starts. <code class="" data-line="">kubectl get pods -A</code> tells you nothing; whatever this is doesn&#8217;t run as a normal pod workload you can just describe.</p>
<h2 id="quick-check-is-anything-actually-loaded-on-this-node">Quick Check: Is Anything Actually Loaded on This Node?</h2>
<pre><code class="" data-line=""># On any cluster node — count loaded eBPF programs
bpftool prog show | wc -l

# Expected output (a cluster running Cilium + Tetragon):
# 47
</code></pre>
<pre><code class="" data-line=""># Break it down by program type
bpftool prog show | grep -oE &#039;^\S+:\s+\K\S+&#039; 2&gt;/dev/null || \
bpftool prog show -j | jq -r &#039;.[].type&#039; | sort | uniq -c

#   12 cgroup_skb      ← Cilium&#039;s per-cgroup socket filtering
#    8 sched_cls       ← TC programs (Cilium&#039;s netdev enforcement, from EP08)
#    6 kprobe          ← Tetragon&#039;s syscall hooks (from EP12)
#    4 tracepoint      ← process/exec tracing (from EP13)
#    2 xdp             ← XDP fast-path filtering (from EP07)
</code></pre>
<blockquote>
<p><strong>Not running Cilium or Tetragon? On EKS or GKE?</strong> The count won&#8217;t be zero even on a &#8220;vanilla&#8221; managed cluster — kube-proxy&#8217;s eBPF mode (if enabled), the CNI&#8217;s own eBPF datapath, and any sidecar-less service mesh all load programs. A count of zero on a production node is itself worth investigating; it usually means you&#8217;re looking at a node pool that hasn&#8217;t finished bootstrapping, or <code class="" data-line="">bpftool</code> is running in a mount namespace that can&#8217;t see the host&#8217;s BPF filesystem.</p>
</blockquote>
<p>Forty-seven loaded programs and no idea which ones matter. That&#8217;s the audit playbook&#8217;s job: turn &#8220;something is loaded&#8221; into &#8220;here is exactly what it is, what it holds, where it enforces, and what it does&#8221; — four commands, in order, no vendor documentation required.</p>
<h2 id="command-1-inventory-whats-loaded-and-who-owns-it">Command 1: Inventory — What&#8217;s Loaded, and Who Owns It</h2>
<p><code class="" data-line="">bpftool prog show</code> lists every eBPF program currently loaded into the kernel on that node, regardless of which process or tool loaded it. The kernel tracks programs independently of the userspace agent that created them — the program keeps running even if that agent&#8217;s pod is deleted.</p>
<pre><code class="" data-line="">bpftool prog show
</code></pre>
<pre><code class="" data-line="">6: cgroup_skb  tag 6deef7357e7b4530  gpl
    loaded_at 2026-06-02T03:14:22+0000  uid 0
    xlated 296B  jited 187B  memlock 4096B  map_ids 4,5
142: sched_cls  name cil_from_netdev  tag a04f5eef06a7f555  gpl
    loaded_at 2026-06-02T03:15:01+0000  uid 0
    xlated 12664B  jited 7532B  memlock 16384B  map_ids 9,10,11,14
    pinned /sys/fs/bpf/tc/globals/cil_from_netdev
201: kprobe  name generic_kprobe_e  tag 88df3d0a1c9e2b41  gpl
    loaded_at 2026-06-02T04:02:18+0000  uid 0
    xlated 3184B  jited 1980B  memlock 8192B  map_ids 22,23
    pinned /sys/fs/bpf/tetragon/generic_kprobe_e
</code></pre>
<blockquote>
<p><strong>Program <code class="" data-line="">tag</code></strong> — a SHA hash of the program&#8217;s instruction stream, computed by the kernel at load time. Two programs with the same tag are running byte-identical bytecode, even if they were loaded by different processes or have different names. It&#8217;s how you confirm two clusters are actually running the same version of a security tool without comparing source.</p>
<p><strong>Pinned path</strong> — a program pinned to <code class="" data-line="">/sys/fs/bpf/...</code> survives after the process that loaded it exits, because the reference is held by a file in the in-kernel BPF filesystem instead of by an open file descriptor in a running process. Most production tools pin their programs; ad hoc programs loaded by a one-off script usually don&#8217;t, and disappear the moment that script&#8217;s process exits.</p>
</blockquote>
<p>The <code class="" data-line="">pinned</code> field is doing most of the audit work here. <code class="" data-line="">/sys/fs/bpf/tc/globals/...</code> is Cilium&#8217;s convention. <code class="" data-line="">/sys/fs/bpf/tetragon/...</code> is Tetragon&#8217;s. Falco&#8217;s kernel-module and eBPF probe modes typically pin under <code class="" data-line="">/sys/fs/bpf/falco*</code>. A program with no <code class="" data-line="">pinned</code> line at all was loaded without a persistent reference — worth asking what process is holding its file descriptor open, because if that process dies, the program unloads.</p>
<blockquote>
<p><strong>For operators (not writing eBPF):</strong> if a security tool&#8217;s DaemonSet pod restarts and its programs <em>don&#8217;t</em> reappear in <code class="" data-line="">bpftool prog show</code> after the container comes back up, that&#8217;s a real signal — the tool failed to re-pin or re-attach, and you&#8217;re running with a gap in coverage even though the pod shows <code class="" data-line="">Running</code>. This is a more reliable health check than the pod&#8217;s own readiness probe, which usually only checks that the userspace agent process is alive.</p>
</blockquote>
<h2 id="command-2-state-what-data-these-programs-are-keeping">Command 2: State — What Data These Programs Are Keeping</h2>
<p>Every <code class="" data-line="">map_ids</code> value in the <code class="" data-line="">prog show</code> output points at a BPF map — the persistent, kernel-resident data structure the program reads or writes on every invocation (see <a href="/ebpf-maps-explained/">eBPF Maps</a> for how these work). <code class="" data-line="">bpftool map show</code> inventories them the same way.</p>
<pre><code class="" data-line="">bpftool map show id 9
</code></pre>
<pre><code class="" data-line="">9: hash  name cilium_lb4_service  flags 0x0
    key 8B  value 24B  max_entries 65536  memlock 6291456B
</code></pre>
<pre><code class="" data-line="">bpftool map show id 22
</code></pre>
<pre><code class="" data-line="">22: lru_hash  name tg_execve_map  flags 0x0
    key 4B  value 128B  max_entries 32768  memlock 12582912B
    pinned /sys/fs/bpf/tetragon/tg_execve_map
</code></pre>
<p>Map ID 9 is a service load-balancer table — 65,536 entries, keyed by a service identifier. Map ID 22 is Tetragon&#8217;s exec cache (the same process-tracking structure covered in <a href="/ebpf-process-lineage-incident-response/">process lineage reconstruction</a>), an LRU hash that evicts its oldest entries once 32,768 processes have been tracked.</p>
<p>The name field alone often tells you what the map is for — <code class="" data-line="">cilium_lb4_service</code>, <code class="" data-line="">tg_execve_map</code> — because most production tools name their maps descriptively rather than leaving them anonymous. When a map has no descriptive name, dump a few entries and read the shape of the data:</p>
<pre><code class="" data-line="">bpftool map dump id 9 | head -5
</code></pre>
<pre><code class="" data-line="">key: 0a 00 00 01 00 00 00 50  value: c0 a8 01 0a 00 00 00 50 00 00 00 01 ...
</code></pre>
<p>Raw bytes without a BTF type description are harder to read, but the sizes still tell you something: an 8-byte key and 24-byte value, repeated 65,536 times, is a fixed-size lookup table — consistent with a service or connection map, not a log or event buffer.</p>
<h2 id="command-3-attachment-where-enforcement-actually-happens">Command 3: Attachment — Where Enforcement Actually Happens</h2>
<p>Inventory and state tell you what&#8217;s loaded and what it remembers. They don&#8217;t tell you where in the packet or syscall path the program actually runs. <code class="" data-line="">bpftool net show</code> answers that for network-attached programs (XDP and TC, from <a href="/ebpf-xdp-kubernetes-networking/">EP07</a> and <a href="/tc-ebpf-kubernetes-network-policy/">EP08</a>); <code class="" data-line="">bpftool cgroup tree</code> answers it for cgroup-attached programs (socket and syscall hooks).</p>
<pre><code class="" data-line="">bpftool net show
</code></pre>
<pre><code class="" data-line="">xdp:
eth0(2) driver id 88 tag 3b185187f1855c4c

tc:
eth0(2) clsact/ingress cil_from_netdev id 142
eth0(2) clsact/egress cil_to_netdev id 143
</code></pre>
<pre><code class="" data-line="">bpftool cgroup tree
</code></pre>
<pre><code class="" data-line="">CgroupPath
ID       AttachType      AttachFlags     Name
/sys/fs/cgroup
         6        cgroup_skb      multi
        18        cgroup_sock_addr multi           cil_sock4_connect
</code></pre>
<p>Program ID 142 — the same <code class="" data-line="">cil_from_netdev</code> you saw in the <code class="" data-line="">prog show</code> output — is attached to <code class="" data-line="">eth0</code>&#8216;s ingress <code class="" data-line="">clsact</code> qdisc. That&#8217;s a direct answer to &#8220;is something making kernel-level policy decisions on this interface&#8221;: yes, at TC ingress, before the packet reaches any userspace process. Program ID 6 (<code class="" data-line="">cgroup_skb</code>) is attached at the root cgroup with <code class="" data-line="">multi</code> flags, meaning it stacks with other programs there rather than replacing them — the enforcement isn&#8217;t exclusive to one tool.</p>
<blockquote>
<p><strong><code class="" data-line="">multi</code> vs exclusive attach flags:</strong> cgroup and TC attachments can either replace whatever was attached before (exclusive) or stack alongside it (<code class="" data-line="">multi</code>/<code class="" data-line="">BPF_F_ALLOW_MULTI</code>). A cluster running more than one eBPF-based tool at the same hook point relies on <code class="" data-line="">multi</code> attachment; if you see an exclusive attach where you expected two tools to coexist, one of them silently lost its hook.</p>
</blockquote>
<h2 id="command-4-behavior-what-it-actually-does">Command 4: Behavior — What It Actually Does</h2>
<p>The first three commands answer what&#8217;s loaded, what it remembers, and where it runs. They don&#8217;t answer what it <em>does</em> — and that matters when the pinned path is missing, unfamiliar, or you don&#8217;t trust it. <code class="" data-line="">bpftool prog dump xlated</code> shows the program&#8217;s instructions after the verifier&#8217;s transformations, in a readable pseudo-assembly.</p>
<pre><code class="" data-line="">bpftool prog dump xlated id 142 | head -12
</code></pre>
<pre><code class="" data-line="">   0: (b7) r0 = 0
   1: (61) r2 = *(u32 *)(r1 +76)
   2: (61) r3 = *(u32 *)(r1 +80)
   3: (bf) r1 = r6
   4: (85) call bpf_skb_load_bytes#26
   5: (16) if w0 == 0x8 goto pc+3
   6: (05) goto pc+9
   7: (61) r1 = *(u32 *)(r6 +0)
   8: (55) r1 != 0x800 goto pc+7
</code></pre>
<p>You don&#8217;t need to hand-trace every instruction to get value out of this. Look for the helper calls — <code class="" data-line="">bpf_skb_load_bytes</code>, <code class="" data-line="">bpf_map_lookup_elem</code>, <code class="" data-line="">bpf_redirect</code>, <code class="" data-line="">bpf_ktime_get_ns</code> — because they name the kernel facilities the program actually touches. A program whose xlated dump is full of <code class="" data-line="">bpf_map_lookup_elem</code> and comparison instructions against <code class="" data-line="">0x800</code> (IPv4&#8217;s EtherType) is doing packet classification. One full of <code class="" data-line="">bpf_probe_read</code> and <code class="" data-line="">bpf_get_current_task</code> is reading process or memory state, not packets — a strong signal you&#8217;re looking at an observability or enforcement hook, not a network one, whatever its pinned path claims.</p>
<blockquote>
<p><strong>For operators (not writing eBPF):</strong> you will not read xlated dumps line by line during an incident. What you&#8217;re checking for is much narrower — does the helper call list match what the tool&#8217;s marketing says it does? A program that claims to be &#8220;read-only observability&#8221; but calls <code class="" data-line="">bpf_skb_store_bytes</code> (which <em>writes</em> packet data) is not read-only. That mismatch is worth escalating before you trust the tool&#8217;s own dashboard.</p>
</blockquote>
<hr />
<h2 id="production-gotchas"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Production Gotchas</h2>
<p><strong><code class="" data-line="">bpftool</code> needs <code class="" data-line="">CAP_BPF</code> or root, and managed nodes don&#8217;t hand that out by default.</strong> On EKS and GKE, you typically can&#8217;t SSH to a node directly. Use <code class="" data-line="">kubectl debug node/&lt;node-name&gt; --image=&lt;image-with-bpftool&gt; -it -- chroot /host</code> to get a privileged shell with host PID and network namespace access, or the cloud provider&#8217;s session-manager equivalent (AWS SSM, <code class="" data-line="">gcloud compute ssh</code>). Confirm the debug image actually ships <code class="" data-line="">bpftool</code> — it&#8217;s not in most minimal base images.</p>
<p><strong>Program IDs are node-local and not stable across restarts.</strong> ID 142 today may be ID 89 after the node reboots and the DaemonSet reloads its programs. Don&#8217;t hardcode IDs in runbooks; always start from <code class="" data-line="">bpftool prog show</code> on the specific node and re-derive the ID for that session.</p>
<p><strong><code class="" data-line="">xlated</code> and <code class="" data-line="">jited</code> dumps require the kernel to have kept the debug info.</strong> Some hardened kernel configs strip <code class="" data-line="">CONFIG_BPF_JIT_ALWAYS_ON</code> debug metadata or disable <code class="" data-line="">kernel.bpf_stats_enabled</code>, in which case <code class="" data-line="">prog dump</code> returns less than shown here. If dumps come back empty, check <code class="" data-line="">sysctl kernel.bpf_stats_enabled</code> before assuming the program itself is hiding something.</p>
<p><strong><code class="" data-line="">bpftool cgroup tree</code> only shows attachments below the cgroup you run it from.</strong> On a Kubernetes node, run it from the root of the host&#8217;s cgroup filesystem (typically after the <code class="" data-line="">chroot /host</code> from the debug pod above), not from inside a container&#8217;s own cgroup namespace, or you&#8217;ll only see a fraction of the attachments.</p>
<p><strong>Pinned paths are a convention, not a guarantee.</strong> Nothing stops a tool from pinning under an unexpected path, or not pinning at all. Treat the pinned-path-to-vendor mapping as a strong hint that narrows your investigation, not as ground truth — confirm ownership with the <code class="" data-line="">tag</code> (command 1) against the vendor&#8217;s published program hashes when it matters for an incident, not just a routine audit.</p>
<hr />
<h2 id="quick-reference">Quick Reference</h2>
<table>
<thead>
<tr>
<th>What you want to know</th>
<th>Command</th>
</tr>
</thead>
<tbody>
<tr>
<td>What&#8217;s loaded</td>
<td><code class="" data-line="">bpftool prog show</code></td>
</tr>
<tr>
<td>Program count by type</td>
<td><code class="" data-line="">bpftool prog show -j \| jq -r &#039;.[].type&#039; \| sort \| uniq -c</code></td>
</tr>
<tr>
<td>What state a program keeps</td>
<td><code class="" data-line="">bpftool map show id &lt;N&gt;</code> (from <code class="" data-line="">map_ids</code> in prog show)</td>
</tr>
<tr>
<td>Sample map contents</td>
<td><code class="" data-line="">bpftool map dump id &lt;N&gt; \| head</code></td>
</tr>
<tr>
<td>Where it&#8217;s attached (network)</td>
<td><code class="" data-line="">bpftool net show</code></td>
</tr>
<tr>
<td>Where it&#8217;s attached (cgroup)</td>
<td><code class="" data-line="">bpftool cgroup tree</code></td>
</tr>
<tr>
<td>What it actually does</td>
<td><code class="" data-line="">bpftool prog dump xlated id &lt;N&gt;</code></td>
</tr>
<tr>
<td>Confirm identical bytecode across nodes</td>
<td>Compare <code class="" data-line="">tag</code> values from <code class="" data-line="">prog show</code></td>
</tr>
<tr>
<td>Privileged shell on a managed node</td>
<td><code class="" data-line="">kubectl debug node/&lt;name&gt; --image=&lt;img&gt; -it -- chroot /host</code></td>
</tr>
</tbody>
</table>
<hr />
<h2 id="key-takeaways">Key Takeaways</h2>
<ul>
<li>Four <code class="" data-line="">bpftool</code> commands audit any eBPF-based tool on any Kubernetes cluster, regardless of vendor: <code class="" data-line="">prog show</code> (inventory), <code class="" data-line="">map show</code> (state), <code class="" data-line="">net show</code>/<code class="" data-line="">cgroup tree</code> (attachment), <code class="" data-line="">prog dump xlated</code> (behavior)</li>
<li>The kernel tracks loaded programs independently of the userspace agent that loaded them — a program&#8217;s pinned path under <code class="" data-line="">/sys/fs/bpf/...</code> usually identifies its owning tool by convention, but that convention is not enforced by the kernel</li>
<li>A program&#8217;s <code class="" data-line="">tag</code> is a hash of its bytecode; matching tags across nodes confirm identical program versions without comparing source or vendor documentation</li>
<li><code class="" data-line="">map_ids</code> in <code class="" data-line="">prog show</code> output link directly to <code class="" data-line="">bpftool map show</code>, letting you trace from &#8220;a program is loaded&#8221; to &#8220;here&#8217;s exactly what data it reads and writes&#8221;</li>
<li><code class="" data-line="">bpftool net show</code> and <code class="" data-line="">cgroup tree</code> answer where enforcement happens in the packet or syscall path — the same question the opening incident needed answered in ten minutes</li>
<li>When the pinned path and tag aren&#8217;t enough, <code class="" data-line="">bpftool prog dump xlated</code> shows the actual kernel helper calls the program makes, which is the only way to confirm behavior when there&#8217;s no documentation to trust</li>
</ul>
<hr />
<h2 id="whats-next">What&#8217;s Next</h2>
<p>EP14 is the audit playbook — the four commands you run in the first ten minutes on any cluster you&#8217;ve inherited, before you trust anything its existing tools tell you about themselves. EP15 goes deeper on one specific case where this matters most: Cilium&#8217;s own policy engine telling you traffic is allowed while packets keep dropping. <code class="" data-line="">bpftool map dump</code> on the right map — not <code class="" data-line="">cilium policy get</code> — is what shows you what&#8217;s actually being enforced.</p>
<p><em>Next: <a href="/cilium-policy-verification-bpftool/">Cilium policy verification — what bpftool shows that cilium policy get doesn&#8217;t</a></em></p>
<p>Get EP15 in your inbox when it publishes → <a href="https://linuxcent.com/subscribe">linuxcent.com/subscribe</a></p>
<p><a class="a2a_button_mastodon" href="https://www.addtoany.com/add_to/mastodon?linkurl=https%3A%2F%2Flinuxcent.com%2Fthe-audit-playbook-four-commands-to-see-any-cluster%2F&amp;linkname=The%20Audit%20Playbook%20%E2%80%94%20Four%20Commands%20to%20See%20Any%20Cluster" title="Mastodon" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_email" href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Flinuxcent.com%2Fthe-audit-playbook-four-commands-to-see-any-cluster%2F&amp;linkname=The%20Audit%20Playbook%20%E2%80%94%20Four%20Commands%20to%20See%20Any%20Cluster" title="Email" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_whatsapp" href="https://www.addtoany.com/add_to/whatsapp?linkurl=https%3A%2F%2Flinuxcent.com%2Fthe-audit-playbook-four-commands-to-see-any-cluster%2F&amp;linkname=The%20Audit%20Playbook%20%E2%80%94%20Four%20Commands%20to%20See%20Any%20Cluster" title="WhatsApp" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_reddit" href="https://www.addtoany.com/add_to/reddit?linkurl=https%3A%2F%2Flinuxcent.com%2Fthe-audit-playbook-four-commands-to-see-any-cluster%2F&amp;linkname=The%20Audit%20Playbook%20%E2%80%94%20Four%20Commands%20to%20See%20Any%20Cluster" title="Reddit" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_x" href="https://www.addtoany.com/add_to/x?linkurl=https%3A%2F%2Flinuxcent.com%2Fthe-audit-playbook-four-commands-to-see-any-cluster%2F&amp;linkname=The%20Audit%20Playbook%20%E2%80%94%20Four%20Commands%20to%20See%20Any%20Cluster" title="X" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Flinuxcent.com%2Fthe-audit-playbook-four-commands-to-see-any-cluster%2F&amp;linkname=The%20Audit%20Playbook%20%E2%80%94%20Four%20Commands%20to%20See%20Any%20Cluster" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_copy_link" href="https://www.addtoany.com/add_to/copy_link?linkurl=https%3A%2F%2Flinuxcent.com%2Fthe-audit-playbook-four-commands-to-see-any-cluster%2F&amp;linkname=The%20Audit%20Playbook%20%E2%80%94%20Four%20Commands%20to%20See%20Any%20Cluster" title="Copy Link" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Flinuxcent.com%2Fthe-audit-playbook-four-commands-to-see-any-cluster%2F&#038;title=The%20Audit%20Playbook%20%E2%80%94%20Four%20Commands%20to%20See%20Any%20Cluster" data-a2a-url="https://linuxcent.com/the-audit-playbook-four-commands-to-see-any-cluster/" data-a2a-title="The Audit Playbook — Four Commands to See Any Cluster"></a></p><p>The post <a href="https://linuxcent.com/the-audit-playbook-four-commands-to-see-any-cluster/">The Audit Playbook — Four Commands to See Any Cluster</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxcent.com/the-audit-playbook-four-commands-to-see-any-cluster/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2228</post-id>	</item>
		<item>
		<title>eBPF Maps — The Persistent Data Layer Between Kernel and Userspace</title>
		<link>https://linuxcent.com/ebpf-maps-explained/</link>
					<comments>https://linuxcent.com/ebpf-maps-explained/#respond</comments>
		
		<dc:creator><![CDATA[Vamshi Krishna Santhapuri]]></dc:creator>
		<pubDate>Thu, 16 Apr 2026 17:57:45 +0000</pubDate>
				<category><![CDATA[eBPF]]></category>
		<category><![CDATA[bpftool]]></category>
		<category><![CDATA[Cilium]]></category>
		<category><![CDATA[eBPF maps]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Observability]]></category>
		<category><![CDATA[SRE]]></category>
		<guid isPermaLink="false">https://linuxcent.com/ebpf-maps-explained/</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 11</span> <span class="rt-label rt-postfix">minutes</span></span>eBPF maps are the persistent data layer between kernel and userspace — hash maps, ring buffers, and LRU maps explained for SREs running Cilium and Falco.</p>
<p>The post <a href="https://linuxcent.com/ebpf-maps-explained/">eBPF Maps — The Persistent Data Layer Between Kernel and Userspace</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 11</span> <span class="rt-label rt-postfix">minutes</span></span><style>
pre{position:relative;background:#1e1e1e;color:#d4d4d4;
    padding:16px 16px 16px 20px;border-radius:6px;overflow-x:auto;
    font-family:'JetBrains Mono','Fira Code','Cascadia Code',Consolas,'Courier New',monospace;
    font-size:.88em;line-height:1.6;border-left:4px solid #555}
code{background:#f4f4f4;padding:2px 5px;border-radius:3px;font-size:.9em}
pre code{background:transparent;padding:0;color:inherit}
pre[data-lang="bash"],pre[data-lang="sh"],
pre[data-lang="shell"],pre[data-lang="zsh"]{border-left-color:#4ec9b0}
pre[data-lang="yaml"],pre[data-lang="json"],
pre[data-lang="toml"],pre[data-lang="xml"]{border-left-color:#569cd6}
pre[data-lang="python"],pre[data-lang="go"],pre[data-lang="rust"],
pre[data-lang="java"],pre[data-lang="c"],pre[data-lang="cpp"]{border-left-color:#c586c0}
pre[data-lang="text"],pre[data-lang="output"],
pre[data-lang="console"]{border-left-color:#888}
.lc-copy-btn{position:absolute;top:8px;right:8px;background:#2d2d2d;color:#ccc;
    border:1px solid #444;border-radius:4px;padding:3px 9px;font-size:.75em;
    font-family:system-ui,sans-serif;cursor:pointer;opacity:0;
    transition:opacity .15s,background .15s;line-height:1.6}
pre:hover .lc-copy-btn{opacity:1}
.lc-copy-btn:hover{background:#3a3a3a;color:#fff}
.lc-copy-btn.copied{color:#4ec9b0;border-color:#4ec9b0}
.lc-lang-badge{position:absolute;top:8px;left:20px;font-family:system-ui,sans-serif;
    font-size:.7em;color:#666;text-transform:uppercase;letter-spacing:.04em;
    line-height:1;pointer-events:none;opacity:0;transition:opacity .15s}
pre:hover .lc-lang-badge{opacity:1}
table{border-collapse:collapse;width:100%;margin:16px 0}
th,td{border:1px solid #ddd;padding:10px 14px;text-align:left}
th{background:#f0f0f0;font-weight:600}
tr:nth-child(even){background:#fafafa}
</style>
<p><script>
(function(){
  if(window.__lcCodeEnhanced)return;
  window.__lcCodeEnhanced=true;
  function enhance(){
    document.querySelectorAll('pre').forEach(function(pre){
      var code=pre.querySelector('code');
      var lang='';
      if(code){var m=(code.className||'').match(/language-(\S+)/);if(m)lang=m[1].toLowerCase();}
      if(lang)pre.setAttribute('data-lang',lang);
      if(lang){var badge=document.createElement('span');badge.className='lc-lang-badge';badge.textContent=lang;pre.insertBefore(badge,pre.firstChild);}
      var btn=document.createElement('button');
      btn.className='lc-copy-btn';btn.textContent='Copy';btn.setAttribute('aria-label','Copy code to clipboard');
      pre.appendChild(btn);
      btn.addEventListener('click',function(){
        var text=code?code.innerText:pre.innerText;
        if(navigator.clipboard&&window.isSecureContext){
          navigator.clipboard.writeText(text).then(function(){ok(btn);}).catch(function(){fb(text,btn);});
        }else{fb(text,btn);}
      });
    });
  }
  function ok(btn){btn.textContent='Copied!';btn.classList.add('copied');setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},2000);}
  function fb(text,btn){
    try{var ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;left:-9999px;top:-9999px;opacity:0';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);ok(btn);}
    catch(e){btn.textContent='✗ Failed';setTimeout(function(){btn.textContent='Copy';},2000);}
  }
  if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',enhance);}else{enhance();}
})();
</script></p>
<p><em>eBPF: From Kernel to Cloud, Episode 5</em><br />
<em><a href="https://linuxcent.com/what-is-ebpf-linux-kubernetes/">What Is eBPF?</a> · <a href="https://linuxcent.com/bpf-verifier-kubernetes-safety/">The BPF Verifier</a> · <a href="https://linuxcent.com/ebpf-vs-kernel-modules-kubernetes/">eBPF vs Kernel Modules</a> · <a href="https://linuxcent.com/ebpf-program-types-kubernetes/">eBPF Program Types</a> · </em><em>eBPF Maps</em>**</p>
<hr />
<p style="font-size:0.72em;font-weight:700;letter-spacing:0.12em;color:#f59e0b;text-transform:uppercase;margin:2em 0 0.75em 0;text-align:center;">Architecture Overview</p>
<figure class="wp-block-image size-full" style="margin:0 0 0.5em 0;">
<img fetchpriority="high" decoding="async" width="1190" height="2560" src="https://linuxcent.com/wp-content/uploads/2026/05/ep05-ebpf-maps-og-2-scaled.png" alt="eBPF Maps — the persistent data layer between kernel eBPF programs and userspace tools" class="wp-image-2112" style="width:100%;height:auto;display:block;border-radius:8px;" srcset="https://linuxcent.com/wp-content/uploads/2026/05/ep05-ebpf-maps-og-2-scaled.png 1190w, https://linuxcent.com/wp-content/uploads/2026/05/ep05-ebpf-maps-og-2-139x300.png 139w, https://linuxcent.com/wp-content/uploads/2026/05/ep05-ebpf-maps-og-2-476x1024.png 476w, https://linuxcent.com/wp-content/uploads/2026/05/ep05-ebpf-maps-og-2-768x1652.png 768w, https://linuxcent.com/wp-content/uploads/2026/05/ep05-ebpf-maps-og-2-714x1536.png 714w, https://linuxcent.com/wp-content/uploads/2026/05/ep05-ebpf-maps-og-2-952x2048.png 952w" sizes="(max-width: 1190px) 100vw, 1190px" /><figcaption style="text-align:center;font-size:0.85em;color:#6b7280;margin-top:0.75em;">eBPF maps are the shared memory between kernel programs and userspace — hash, array, ringbuf, and LRU variants shown.</figcaption></figure>
<hr style="border:none;border-top:1px solid #e5e7eb;margin:0.5em 0 2em 0;"/>
<h2 id="tldr">TL;DR</h2>
<ul>
<li>eBPF programs are stateless — maps are where all state lives, between invocations and between kernel and userspace<br />
<em>(&#8220;stateless&#8221; here means each program invocation starts with no memory of previous runs — like a function with no global variables)</em></li>
<li>Every production eBPF tool (Cilium, Falco, Tetragon, Datadog NPM) is a map-based architecture — <code class="" data-line="">bpftool map list</code> shows you what it&#8217;s actually holding</li>
<li>Per-CPU maps eliminate write contention for high-frequency counters; the tool aggregates per-CPU values at export time</li>
<li>LRU maps handle unbounded key spaces (IPs, PIDs, connections) without hard errors when full — but eviction is silent, so size generously</li>
<li>Ring buffer (kernel 5.8+) is the correct event streaming primitive — Falco and Tetragon both use it</li>
<li>Map memory is kernel-locked and invisible to standard memory metrics — account for it explicitly on eBPF-heavy nodes</li>
<li>Pinned maps survive restarts; Cilium uses this for zero-disruption connection tracking through upgrades</li>
</ul>
<hr />
<h2 id="the-big-picture">The Big Picture</h2>
<pre><code class="" data-line="">  HOW eBPF MAPS CONNECT KERNEL PROGRAMS TO USERSPACE TOOLS

  ┌─────────────────────────────────────────────────────────────┐
  │  Kernel space                                               │
  │                                                             │
  │  [XDP program]  [TC program]  [kprobe]  [tracepoint]        │
  │        │              │           │           │             │
  │        └──────────────┴───────────┴───────────┘             │
  │                              │                              │
  │                   bpf_map_update_elem()                     │
  │                              │                              │
  │                              ▼                              │
  │  ┌─────────────────────────────────────────────────────┐    │
  │  │             eBPF MAP (kernel object)                │    │
  │  │  hash · percpu_hash · lru_hash · ringbuf · lpm_trie │    │
  │  │  Lives outside program invocations.                 │    │
  │  │  Pinned maps (/sys/fs/bpf/) survive restarts.       │    │
  │  └────────────────────┬────────────────────────────────┘    │
  └───────────────────────│─────────────────────────────────────┘
                          │  read / write via file descriptor
                          ▼
  ┌─────────────────────────────────────────────────────────────┐
  │  Userspace tools                                            │
  │                                                             │
  │  Cilium agent  Falco engine  Tetragon  bpftool map dump     │
  └─────────────────────────────────────────────────────────────┘
</code></pre>
<hr />
<p>eBPF maps are the persistent data layer between kernel programs and the tools that consume their output. eBPF programs fire and exit — there&#8217;s no memory between invocations. Yet Cilium tracks TCP connections across millions of packets, and Falco correlates a process exec from five minutes ago with a suspicious network connection happening now. The mechanism between stateless kernel programs and the stateful production tools you depend on is what this episode is about — and understanding it changes what you see when you run <code class="" data-line="">bpftool map list</code>.</p>
<hr />
<p>I was trying to identify the noisy neighbor saturating a cluster&#8217;s egress link. I had an eBPF program loading cleanly, events firing, everything confirming it was working. But when I read back the per-port connection counters from userspace, everything was zero.</p>
<p>I spent an hour on it before posting to the BCC mailing list. The reply came back fast: eBPF programs don&#8217;t hold state between invocations. Every time the kprobe fires, the program starts fresh. The counter I was incrementing existed only for that single call — created, incremented to one, then discarded. On every single invocation. I was counting events one at a time, throwing the count away, and reading nothing.</p>
<p>That&#8217;s what eBPF maps solve.</p>
<h2 id="quick-check-what-maps-are-running-on-your-node">Quick Check: What Maps Are Running on Your Node?</h2>
<p>Before the map types walkthrough — see the live state of maps on any cluster node right now:</p>
<pre><code class="" data-line=""># SSH into a worker node, then:
bpftool map list
</code></pre>
<p>On a node running Cilium + Falco, you&#8217;ll see something like:</p>
<pre><code class="" data-line="">12: hash          name cilium_ct4_glo    key 24B  value 56B  max_entries 65536  memlock 5767168B
13: lpm_trie      name cilium_ipcache    key 40B  value 32B  max_entries 512000 memlock 327680B
14: percpu_hash   name cilium_metrics    key 8B   value 32B  max_entries 65536  memlock 2097152B
28: ringbuf       name falco_events      max_entries 8388608
</code></pre>
<p>Reading this output:<br />
&#8211; <code class="" data-line="">hash</code>, <code class="" data-line="">lpm_trie</code>, <code class="" data-line="">percpu_hash</code>, <code class="" data-line="">ringbuf</code> — the map <em>type</em> (each optimised for a different access pattern)<br />
&#8211; <code class="" data-line="">key 24B value 56B</code> — sizes of a single entry&#8217;s key and value in bytes<br />
&#8211; <code class="" data-line="">max_entries</code> — the hard ceiling; when the map is full, behaviour depends on type (see LRU section below)<br />
&#8211; <code class="" data-line="">memlock</code> — non-pageable kernel memory this map consumes (invisible to <code class="" data-line="">free</code> and container metrics)</p>
<blockquote>
<p><strong>Not running Cilium?</strong> On EKS with <code class="" data-line="">aws-vpc-cni</code> or GKE with <code class="" data-line="">kubenet</code>, there are far fewer maps here — primarily kube-proxy uses iptables rather than BPF maps. Running <code class="" data-line="">bpftool map list</code> still works; you&#8217;ll just see fewer entries. On a pure iptables-based cluster, most of the maps you see come from the system kernel itself, not a CNI.</p>
</blockquote>
<h2 id="maps-are-the-architecture-not-an-afterthought">Maps Are the Architecture, Not an Afterthought</h2>
<p>Maps are kernel objects that live outside any individual program invocation. They&#8217;re shared between multiple eBPF programs, readable and writable from userspace, and persistent for the lifetime of the map — which can outlive both the program that created them and the userspace process that loaded them.</p>
<p>Every production eBPF tool is fundamentally a map-based architecture:</p>
<ul>
<li>Cilium stores connection tracking state in BPF hash maps</li>
<li>Falco uses ring buffers to stream syscall events to its userspace rule engine</li>
<li>Tetragon maintains process tree state across exec events using maps</li>
<li>Datadog NPM stores per-connection flow stats in per-CPU maps for lock-free metric accumulation</li>
</ul>
<p>Run <code class="" data-line="">bpftool map list</code> on a Cilium node:</p>
<pre><code class="" data-line="">$ bpftool map list
ID 12: hash          name cilium_ct4_glo    key 24B  value 56B   max_entries 65536
#      ^^^^           ^^^^^^^^^^^^^^^^       ^^^^^^   ^^^^^^^     ^^^^^^^^^^^^^^^^
#      type           map name               key size value size  max concurrent entries

ID 13: lpm_trie      name cilium_ipcache    key 40B  value 32B   max_entries 512000
#      longest-prefix-match trie — for IP address + CIDR lookups

ID 14: percpu_hash   name cilium_metrics    key 8B   value 32B   max_entries 65536
#      one copy of this map per CPU — no write contention for high-frequency counters

ID 28: ringbuf       name falco_events      max_entries 8388608
#                                           ^^^^^^^^^^^ 8MB ring buffer for event streaming
</code></pre>
<p>Connection tracking, IP policy cache, per-CPU metrics, event stream. Every one of these is a different map type, chosen for a specific reason.</p>
<h2 id="map-types-and-what-theyre-actually-used-for">Map Types and What They&#8217;re Actually Used For</h2>
<h3 id="hash-maps">Hash Maps</h3>
<p>The general-purpose key-value store. A key maps to a value — lookup is O(1) average. Cilium&#8217;s connection tracking map (<code class="" data-line="">cilium_ct4_glo</code>) is a hash map: the key is a 5-tuple (source IP, destination IP, ports, protocol), the value is the connection state.</p>
<pre><code class="" data-line="">$ bpftool map show id 12
12: hash  name cilium_ct4_glo  flags 0x0
        key 24B  value 56B  max_entries 65536  memlock 5767168B
</code></pre>
<p>The <code class="" data-line="">key 24B</code> is the 5-tuple. The <code class="" data-line="">value 56B</code> is the connection state record. <code class="" data-line="">max_entries 65536</code> is the upper bound — Cilium can track 65,536 active connections in this map before hitting the limit.</p>
<p>Hash maps are shared across all CPUs on the node. When multiple CPUs try to update the same entry simultaneously — which happens constantly on busy nodes — writes need to be coordinated. For most use cases this is fine. For high-frequency counters updated on every packet, it&#8217;s a bottleneck. That&#8217;s when you reach for a per-CPU hash map.</p>
<p><strong>Where you see them:</strong> connection tracking, per-IP statistics, process-to-identity mapping, policy verdict caching.</p>
<h3 id="per-cpu-hash-maps">Per-CPU Hash Maps</h3>
<p>Per-CPU hash maps solve the write coordination problem by giving each CPU its own independent copy of every entry. There&#8217;s no sharing, no contention, no waiting — each CPU writes its own copy without touching any other.</p>
<p>The tradeoff: reading from userspace means collecting one value per CPU and summing them up. That aggregation happens in the tool, not the kernel.</p>
<pre><code class="" data-line=""># Cilium&#039;s per-CPU metrics map — one counter value per CPU
bpftool map dump id 14
key: 0x00000001
  value (CPU 00): 12345
  value (CPU 01): 8901
  value (CPU 02): 3421
  value (CPU 03): 7102
# total bytes for this metric: 31769
</code></pre>
<p>Cilium&#8217;s <code class="" data-line="">cilium_metrics</code> map uses this pattern for exactly this reason — it&#8217;s updated on every packet across every CPU on the node. Forcing all CPUs to coordinate writes to a single shared entry at that rate would hurt throughput. Instead: each CPU writes locally, Cilium&#8217;s userspace agent sums the values at export time.</p>
<p><strong>Where you see them:</strong> packet counters, byte counters, syscall frequency metrics — anywhere updates happen on every event at high volume.</p>
<h3 id="lru-hash-maps">LRU Hash Maps</h3>
<p>LRU hash maps add automatic eviction. Same key-value semantics as a regular hash map, but when the map hits its entry limit, the least recently accessed entry is dropped to make room for the new one.</p>
<p>This matters for any map tracking dynamic state with an unpredictable number of keys: TCP connections, process IDs, DNS queries, pod IPs. Without LRU semantics, a full map returns an error on insert — and in production, that means your tool silently stops tracking new entries. Not a crash, not an alert — just missing data.</p>
<p>Cilium&#8217;s connection tracking map is LRU-bounded at 65,536 entries. On a node handling high-connection-rate workloads, this can fill up. When it does, Cilium starts evicting old connections to make room for new ones — and if it&#8217;s evicting too aggressively, you&#8217;ll see connection resets.</p>
<pre><code class="" data-line=""># Check current CT map usage vs its limit
bpftool map show id 12
# max_entries tells you the ceiling
# count entries to see current usage
bpftool map dump id 12 | grep -c &quot;^key&quot;
</code></pre>
<p>Size LRU maps at 2× your expected concurrent active entries. Aggressive eviction under pressure introduces gaps — not crashes, but missing or incorrect state.</p>
<p><strong>Where you see them:</strong> connection tracking, process lineage, anything where the key space is dynamic and unbounded.</p>
<h3 id="ring-buffers">Ring Buffers</h3>
<p>Ring buffers are how eBPF tools stream events from the kernel to a userspace consumer. Falco reads syscall events from a ring buffer. Tetragon streams process execution and network events through ring buffers. The pattern is the same across all of them:</p>
<pre><code class="" data-line="">kernel eBPF program
  → sees event (syscall, network packet, process exec)
  → writes record to ring buffer
  → userspace tool reads it and processes (Falco rules, Tetragon policies)
</code></pre>
<p>What makes ring buffers the right primitive for event streaming:</p>
<ul>
<li><strong>Single buffer shared across all CPUs</strong> — unlike the older <code class="" data-line="">perf_event_array</code> approach which required one buffer per CPU, a ring buffer is one allocation, one file descriptor, one consumer</li>
<li><strong>Lock-free</strong> — the kernel writes, the userspace tool reads, they don&#8217;t block each other</li>
<li><strong>Backpressure when full</strong> — if the userspace tool can&#8217;t keep up, new events are dropped rather than queued indefinitely. The tool can detect and count drops. Falco reports these as <code class="" data-line="">Dropped events</code> in its stats output.</li>
</ul>
<pre><code class="" data-line=""># Falco&#039;s ring buffer — 8MB
bpftool map list | grep ringbuf
# ID 28: ringbuf  name falco_events  max_entries 8388608
</code></pre>
<p>8,388,608 bytes = 8MB. That&#8217;s the buffer between Falco&#8217;s kernel hooks and its rule engine. If there&#8217;s a burst of syscall activity and Falco&#8217;s rule evaluation can&#8217;t keep up, events drop into that window and are lost.</p>
<p>Sizing matters operationally. Too small and you drop events during normal burst. Too large and you&#8217;re holding non-pageable kernel memory that doesn&#8217;t show up in standard memory metrics.</p>
<pre><code class="" data-line=""># Check Falco&#039;s drop rate
falcoctl stats
# or check the Falco logs
journalctl -u falco | grep -i &quot;drop&quot;
</code></pre>
<p>Most production deployments run 8–32MB. Start at 8MB, monitor drop rates under load, size up if needed.</p>
<p><strong>Where you see them:</strong> Falco event streaming, Tetragon audit events, any tool that needs to move high-volume event data from kernel to userspace.</p>
<h3 id="array-maps">Array Maps</h3>
<p>Array maps are fixed-size, integer-indexed, and entirely pre-allocated at creation time. Think of them as lookup tables with integer keys — constant-time access, no hash overhead, no dynamic allocation.</p>
<p>Cilium uses array maps for policy configuration: a fixed set of slots indexed by endpoint identity number. When a packet arrives and Cilium needs to check policy, it indexes into the array directly rather than doing a hash lookup. For read-heavy, write-rare data, this is faster.</p>
<p>The constraint: you can&#8217;t delete entries from an array map. Every slot exists for the lifetime of the map. If you need to track state that comes and goes — connections, processes, pods — use a hash map instead.</p>
<p><strong>Where you see them:</strong> policy configuration, routing tables with fixed indices, per-CPU stats indexed by CPU number.</p>
<h3 id="lpm-trie-maps">LPM Trie Maps</h3>
<p>LPM (Longest Prefix Match) trie maps handle IP prefix lookups — the same operation that a hardware router does when deciding which interface to send a packet out of.</p>
<p>You can store a mix of specific host addresses (/32) and CIDR ranges (/16, /24) in the same map, and a lookup returns the most specific match. If <code class="" data-line="">10.0.1.15/32</code> and <code class="" data-line="">10.0.0.0/8</code> are both in the map, a lookup for <code class="" data-line="">10.0.1.15</code> returns the /32 entry.</p>
<p>Cilium&#8217;s <code class="" data-line="">cilium_ipcache</code> map is an LPM trie. It maps every IP in the cluster to its security identity — the identifier Cilium uses for policy enforcement. When a packet arrives, Cilium does a trie lookup on the source IP to find out which endpoint sent it, then checks policy against that identity.</p>
<pre><code class="" data-line=""># Inspect the ipcache map
bpftool map show id 13
# lpm_trie  name cilium_ipcache  key 40B  value 32B  max_entries 512000

# Look up which security identity owns a pod IP
bpftool map lookup id 13 key hex 20 00 00 00 0a 00 01 0f 00 00 00 00 00 00 00 00 00 00 00 00
</code></pre>
<p><strong>Where you see them:</strong> IP-to-identity mapping (Cilium), CIDR-based policy enforcement, IP blocklists.</p>
<hr />
<h2 id="pinned-maps-state-that-survives-restarts">Pinned Maps — State That Survives Restarts</h2>
<p>By default, a map&#8217;s lifetime is tied to the tool that created it. When the tool exits, the kernel garbage-collects the map.</p>
<p>Pinning writes a reference to the BPF filesystem at <code class="" data-line="">/sys/fs/bpf</code>, which keeps the map alive even after the creating process exits:</p>
<pre><code class="" data-line=""># See all maps Cilium has pinned
ls /sys/fs/bpf/tc/globals/
# cilium_ct4_global  cilium_ipcache  cilium_metrics  cilium_policy ...

# Inspect a pinned map directly — no Cilium process needed
bpftool map dump pinned /sys/fs/bpf/tc/globals/cilium_ct4_global

# Pin any map by ID for manual inspection
bpftool map pin id 12 /sys/fs/bpf/my_conn_tracker
bpftool map dump pinned /sys/fs/bpf/my_conn_tracker
</code></pre>
<p>Cilium pins all its maps under <code class="" data-line="">/sys/fs/bpf/tc/globals/</code>. When Cilium restarts — rolling upgrade, crash, OOM kill — it reopens its pinned maps and resumes with existing state intact. Pods maintain established TCP connections through a Cilium restart without disruption.</p>
<p>This is operationally significant: if you&#8217;re evaluating eBPF-based tools for production, check whether they pin their maps. A tool that doesn&#8217;t loses all its tracked state on every restart — connection tracking resets, process lineage gaps, policy state rebuilt from scratch.</p>
<hr />
<h2 id="map-memory-a-production-consideration">Map Memory: A Production Consideration</h2>
<p>Map memory is kernel-locked — it cannot be paged out, and it doesn&#8217;t show up in standard memory pressure metrics. Your node&#8217;s <code class="" data-line="">free</code> output and container memory limits don&#8217;t account for it.</p>
<blockquote>
<p><strong>Kernel-locked memory</strong> is memory the OS guarantees will never be swapped to disk — it stays in RAM permanently. The kernel requires this for eBPF maps because a kernel program running during a network interrupt cannot wait for a page fault. The side effect: it doesn&#8217;t appear in <code class="" data-line="">top</code>, <code class="" data-line="">free</code>, or container memory metrics, so it&#8217;s easy to accidentally provision nodes without accounting for it.</p>
</blockquote>
<pre><code class="" data-line=""># Total eBPF map memory locked on this node
bpftool map list -j | python3 -c &quot;
import json,sys
maps=json.load(sys.stdin)
total=sum(m.get(&#039;bytes_memlock&#039;,0) for m in maps)
print(f&#039;Total map memory: {total/1024/1024:.1f} MB&#039;)
&quot;

# Check system memlock limit (unlimited is correct for eBPF tools)
ulimit -l

# Check what Cilium&#039;s systemd unit sets
systemctl show cilium | grep -i memlock
</code></pre>
<p>On a node running Cilium + Falco + Datadog NPM, I&#8217;ve seen 200–400MB of map memory locked. That&#8217;s real, non-pageable kernel memory. If you&#8217;re sizing nodes for eBPF-heavy workloads, account for this separately from your pod workload memory.</p>
<p>If an eBPF tool fails to load with a permission error despite having enough free memory, the root cause is usually the <code class="" data-line="">memlock</code> ulimit for the process. Cilium, Falco, and most production tools set <code class="" data-line="">LimitMEMLOCK=infinity</code> in their systemd units. Verify this if you&#8217;re deploying a new eBPF-based tool and seeing unexpected load failures.</p>
<hr />
<h2 id="inspecting-maps-in-production">Inspecting Maps in Production</h2>
<pre><code class="" data-line=""># List all maps: type, name, key/value sizes, memory usage
bpftool map list

# Dump all entries in a map (careful with large maps)
bpftool map dump id 12

# Look up a specific entry by key
bpftool map lookup id 12 key hex 0a 00 01 0f 00 00 00 00

# Watch map stats live
watch -n1 &#039;bpftool map show id 12&#039;

# See all maps for a specific tool by checking its pinned path
ls /sys/fs/bpf/tc/globals/                    # Cilium
ls /sys/fs/bpf/falco/                         # Falco (if pinned)

# Cross-reference map IDs with the programs using them
bpftool prog list
bpftool map list
</code></pre>
<hr />
<h2 id="production-gotchas"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Production Gotchas</h2>
<p><strong>A full LRU map drops state silently, not loudly</strong><br />
When Cilium&#8217;s CT map fills up, it starts evicting the least recently used connections — not returning an error. You see connection resets, not a tool alert. Check map utilisation (<code class="" data-line="">bpftool map dump id X | grep -c key</code>) against <code class="" data-line="">max_entries</code> on nodes with high connection rates.</p>
<p><strong>Ring buffer drops don&#8217;t stop the tool — they create gaps</strong><br />
When Falco&#8217;s ring buffer fills up, events are dropped. Falco keeps running. The rule engine keeps processing. But you have gaps in your syscall visibility. Monitor <code class="" data-line="">Dropped events</code> in Falco&#8217;s stats and size the ring buffer accordingly.</p>
<p><strong>Map memory is invisible to standard monitoring</strong><br />
200–400MB of kernel-locked memory on a Cilium + Falco node doesn&#8217;t appear in <code class="" data-line="">top</code>, container memory metrics, or memory pressure alerts. Size eBPF-heavy nodes with this in mind and add explicit map memory monitoring via <code class="" data-line="">bpftool</code>.</p>
<p><strong>Tools that don&#8217;t pin their maps lose state on restart</strong><br />
A Cilium restart with pinned maps = zero-disruption connection tracking. A tool without pinning = all tracked state rebuilt from scratch. This matters for connection tracking tools and any tool maintaining process lineage.</p>
<p><strong><code class="" data-line="">perf_event_array</code> on kernel 5.8+ is the old way</strong><br />
Older eBPF tools use per-CPU <code class="" data-line="">perf_event_array</code> for event streaming. Ring buffer is strictly better — single allocation, lower overhead, simpler consumption. If you&#8217;re running a tool that still uses <code class="" data-line="">perf_event_array</code> on a 5.8+ kernel, it&#8217;s using a legacy path.</p>
<hr />
<h2 id="key-takeaways">Key Takeaways</h2>
<ul>
<li>eBPF programs are stateless — maps are where all state lives, between invocations and between kernel and userspace</li>
<li>Every production eBPF tool (Cilium, Falco, Tetragon, Datadog NPM) is a map-based architecture — <code class="" data-line="">bpftool map list</code> shows you what it&#8217;s actually holding</li>
<li>Per-CPU maps eliminate write contention for high-frequency counters; the tool aggregates per-CPU values at export time</li>
<li>LRU maps handle unbounded key spaces (IPs, PIDs, connections) without hard errors when full — but eviction is silent, so size generously</li>
<li>Ring buffer (kernel 5.8+) is the correct event streaming primitive — Falco and Tetragon both use it</li>
<li>Map memory is kernel-locked and invisible to standard memory metrics — account for it explicitly on eBPF-heavy nodes</li>
<li>Pinned maps survive restarts; Cilium uses this for zero-disruption connection tracking through upgrades</li>
</ul>
<hr />
<h2 id="whats-next">What&#8217;s Next</h2>
<p>You know what program types run in the kernel, and you know how they hold state.</p>
<p>Get EP06 in your inbox when it publishes → <a href="https://linuxcent.com/subscribe">linuxcent.com/subscribe</a> But there&#8217;s a problem anyone running eBPF-based tools eventually runs into: a tool works on one kernel version and breaks on the next. Struct layouts shift between patch versions. Field offsets move. EP06 covers CO-RE (Compile Once, Run Everywhere) and libbpf — the mechanism that makes tools like Cilium and Falco survive your node upgrades without recompilation, and why kernel version compatibility is a solved problem for any tool built on this toolchain.</p>
<p><a class="a2a_button_mastodon" href="https://www.addtoany.com/add_to/mastodon?linkurl=https%3A%2F%2Flinuxcent.com%2Febpf-maps-explained%2F&amp;linkname=eBPF%20Maps%20%E2%80%94%20The%20Persistent%20Data%20Layer%20Between%20Kernel%20and%20Userspace" title="Mastodon" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_email" href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Flinuxcent.com%2Febpf-maps-explained%2F&amp;linkname=eBPF%20Maps%20%E2%80%94%20The%20Persistent%20Data%20Layer%20Between%20Kernel%20and%20Userspace" title="Email" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_whatsapp" href="https://www.addtoany.com/add_to/whatsapp?linkurl=https%3A%2F%2Flinuxcent.com%2Febpf-maps-explained%2F&amp;linkname=eBPF%20Maps%20%E2%80%94%20The%20Persistent%20Data%20Layer%20Between%20Kernel%20and%20Userspace" title="WhatsApp" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_reddit" href="https://www.addtoany.com/add_to/reddit?linkurl=https%3A%2F%2Flinuxcent.com%2Febpf-maps-explained%2F&amp;linkname=eBPF%20Maps%20%E2%80%94%20The%20Persistent%20Data%20Layer%20Between%20Kernel%20and%20Userspace" title="Reddit" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_x" href="https://www.addtoany.com/add_to/x?linkurl=https%3A%2F%2Flinuxcent.com%2Febpf-maps-explained%2F&amp;linkname=eBPF%20Maps%20%E2%80%94%20The%20Persistent%20Data%20Layer%20Between%20Kernel%20and%20Userspace" title="X" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Flinuxcent.com%2Febpf-maps-explained%2F&amp;linkname=eBPF%20Maps%20%E2%80%94%20The%20Persistent%20Data%20Layer%20Between%20Kernel%20and%20Userspace" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_copy_link" href="https://www.addtoany.com/add_to/copy_link?linkurl=https%3A%2F%2Flinuxcent.com%2Febpf-maps-explained%2F&amp;linkname=eBPF%20Maps%20%E2%80%94%20The%20Persistent%20Data%20Layer%20Between%20Kernel%20and%20Userspace" title="Copy Link" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Flinuxcent.com%2Febpf-maps-explained%2F&#038;title=eBPF%20Maps%20%E2%80%94%20The%20Persistent%20Data%20Layer%20Between%20Kernel%20and%20Userspace" data-a2a-url="https://linuxcent.com/ebpf-maps-explained/" data-a2a-title="eBPF Maps — The Persistent Data Layer Between Kernel and Userspace"></a></p><p>The post <a href="https://linuxcent.com/ebpf-maps-explained/">eBPF Maps — The Persistent Data Layer Between Kernel and Userspace</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxcent.com/ebpf-maps-explained/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1481</post-id>	</item>
		<item>
		<title>eBPF Program Types — What&#8217;s Actually Running on Your Nodes</title>
		<link>https://linuxcent.com/ebpf-program-types-kubernetes/</link>
					<comments>https://linuxcent.com/ebpf-program-types-kubernetes/#respond</comments>
		
		<dc:creator><![CDATA[Vamshi Krishna Santhapuri]]></dc:creator>
		<pubDate>Sun, 12 Apr 2026 09:51:22 +0000</pubDate>
				<category><![CDATA[eBPF]]></category>
		<category><![CDATA[bpftool]]></category>
		<category><![CDATA[Cilium]]></category>
		<category><![CDATA[eBPF program types]]></category>
		<category><![CDATA[Falco]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[SRE]]></category>
		<guid isPermaLink="false">https://linuxcent.com/ebpf-program-types-kubernetes/</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 8</span> <span class="rt-label rt-postfix">minutes</span></span>What eBPF program types run on your Kubernetes nodes — XDP, TC, tracepoints, LSM explained through real SRE incidents using bpftool, Cilium, and Falco.</p>
<p>The post <a href="https://linuxcent.com/ebpf-program-types-kubernetes/">eBPF Program Types — What&#8217;s Actually Running on Your Nodes</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 8</span> <span class="rt-label rt-postfix">minutes</span></span><style>
pre{position:relative;background:#1e1e1e;color:#d4d4d4;
    padding:16px 16px 16px 20px;border-radius:6px;overflow-x:auto;
    font-family:'JetBrains Mono','Fira Code','Cascadia Code',Consolas,'Courier New',monospace;
    font-size:.88em;line-height:1.6;border-left:4px solid #555}
code{background:#f4f4f4;padding:2px 5px;border-radius:3px;font-size:.9em}
pre code{background:transparent;padding:0;color:inherit}
pre[data-lang="bash"],pre[data-lang="sh"],
pre[data-lang="shell"],pre[data-lang="zsh"]{border-left-color:#4ec9b0}
pre[data-lang="yaml"],pre[data-lang="json"],
pre[data-lang="toml"],pre[data-lang="xml"]{border-left-color:#569cd6}
pre[data-lang="python"],pre[data-lang="go"],pre[data-lang="rust"],
pre[data-lang="java"],pre[data-lang="c"],pre[data-lang="cpp"]{border-left-color:#c586c0}
pre[data-lang="text"],pre[data-lang="output"],
pre[data-lang="console"]{border-left-color:#888}
.lc-copy-btn{position:absolute;top:8px;right:8px;background:#2d2d2d;color:#ccc;
    border:1px solid #444;border-radius:4px;padding:3px 9px;font-size:.75em;
    font-family:system-ui,sans-serif;cursor:pointer;opacity:0;
    transition:opacity .15s,background .15s;line-height:1.6}
pre:hover .lc-copy-btn{opacity:1}
.lc-copy-btn:hover{background:#3a3a3a;color:#fff}
.lc-copy-btn.copied{color:#4ec9b0;border-color:#4ec9b0}
.lc-lang-badge{position:absolute;top:8px;left:20px;font-family:system-ui,sans-serif;
    font-size:.7em;color:#666;text-transform:uppercase;letter-spacing:.04em;
    line-height:1;pointer-events:none;opacity:0;transition:opacity .15s}
pre:hover .lc-lang-badge{opacity:1}
table{border-collapse:collapse;width:100%;margin:16px 0}
th,td{border:1px solid #ddd;padding:10px 14px;text-align:left}
th{background:#f0f0f0;font-weight:600}
tr:nth-child(even){background:#fafafa}
</style>
<p><script>
(function(){
  if(window.__lcCodeEnhanced)return;
  window.__lcCodeEnhanced=true;
  function enhance(){
    document.querySelectorAll('pre').forEach(function(pre){
      var code=pre.querySelector('code');
      var lang='';
      if(code){var m=(code.className||'').match(/language-(\S+)/);if(m)lang=m[1].toLowerCase();}
      if(lang)pre.setAttribute('data-lang',lang);
      if(lang){var badge=document.createElement('span');badge.className='lc-lang-badge';badge.textContent=lang;pre.insertBefore(badge,pre.firstChild);}
      var btn=document.createElement('button');
      btn.className='lc-copy-btn';btn.textContent='Copy';btn.setAttribute('aria-label','Copy code to clipboard');
      pre.appendChild(btn);
      btn.addEventListener('click',function(){
        var text=code?code.innerText:pre.innerText;
        if(navigator.clipboard&&window.isSecureContext){
          navigator.clipboard.writeText(text).then(function(){ok(btn);}).catch(function(){fb(text,btn);});
        }else{fb(text,btn);}
      });
    });
  }
  function ok(btn){btn.textContent='Copied!';btn.classList.add('copied');setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},2000);}
  function fb(text,btn){
    try{var ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;left:-9999px;top:-9999px;opacity:0';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);ok(btn);}
    catch(e){btn.textContent='✗ Failed';setTimeout(function(){btn.textContent='Copy';},2000);}
  }
  if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',enhance);}else{enhance();}
})();
</script></p>
<p><em>eBPF: From Kernel to Cloud, Episode 4</em><br />
<em><a href="https://linuxcent.com/what-is-ebpf-linux-kubernetes/">What Is eBPF?</a> · <a href="https://linuxcent.com/bpf-verifier-kubernetes-safety/">The BPF Verifier</a> · <a href="https://linuxcent.com/ebpf-vs-kernel-modules-kubernetes/">eBPF vs Kernel Modules</a> · </em><em>eBPF Program Types</em>**</p>
<hr />
<p style="font-size:0.72em;font-weight:700;letter-spacing:0.12em;color:#f59e0b;text-transform:uppercase;margin:2em 0 0.75em 0;text-align:center;">Architecture Overview</p>
<figure class="wp-block-image size-full" style="margin:0 0 0.5em 0;">
<img decoding="async" width="2400" height="1828" src="https://linuxcent.com/wp-content/uploads/2026/05/ep04-ebpf-program-types-og-2.png" alt="eBPF Program Types — tracing, networking, and security hook points across the Linux kernel" class="wp-image-2111" style="width:100%;height:auto;display:block;border-radius:8px;" srcset="https://linuxcent.com/wp-content/uploads/2026/05/ep04-ebpf-program-types-og-2.png 2400w, https://linuxcent.com/wp-content/uploads/2026/05/ep04-ebpf-program-types-og-2-300x229.png 300w, https://linuxcent.com/wp-content/uploads/2026/05/ep04-ebpf-program-types-og-2-1024x780.png 1024w, https://linuxcent.com/wp-content/uploads/2026/05/ep04-ebpf-program-types-og-2-768x585.png 768w, https://linuxcent.com/wp-content/uploads/2026/05/ep04-ebpf-program-types-og-2-1536x1170.png 1536w, https://linuxcent.com/wp-content/uploads/2026/05/ep04-ebpf-program-types-og-2-2048x1560.png 2048w" sizes="(max-width: 2400px) 100vw, 2400px" /><figcaption style="text-align:center;font-size:0.85em;color:#6b7280;margin-top:0.75em;">Each eBPF program type attaches to a different kernel hook — from socket filters to LSM enforcement points.</figcaption></figure>
<hr style="border:none;border-top:1px solid #e5e7eb;margin:0.5em 0 2em 0;"/>
<h2 id="tldr">TL;DR</h2>
<ul>
<li><code class="" data-line="">bpftool prog list</code> and <code class="" data-line="">bpftool net list</code> show every eBPF program on a node — run these first when debugging eBPF-based tool behavior</li>
<li>TC programs can stack on the same interface; stale programs from incomplete Cilium upgrades cause intermittent packet drops — check <code class="" data-line="">tc filter show</code> after every Cilium upgrade</li>
<li>XDP fires before <code class="" data-line="">sk_buff</code> allocation — fastest hook, but no pod identity; Cilium uses it for service load balancing, not pod policy</li>
<li>XDP silently falls back to generic mode on unsupported NICs — verify with <code class="" data-line="">ip link show | grep xdp</code></li>
<li>Tracepoints are stable across kernel versions; kprobe-based tools may silently break after node OS patches</li>
<li>LSM hooks enforce at the kernel level — what makes Tetragon&#8217;s enforcement mode fundamentally different from sidecar-based approaches</li>
</ul>
<hr />
<h2 id="the-big-picture">The Big Picture</h2>
<pre><code class="" data-line="">  WHERE eBPF PROGRAM TYPES ATTACH IN THE KERNEL

  NIC hardware
       ↓
  DMA → ring buffer
       ↓
  ┌─────────────────────────────────────────────────┐
  │  XDP hook  (Cilium: service load balancing)     │
  │  Sees: raw packet bytes only. No pod identity.  │
  └─────────────────────────┬───────────────────────┘
                            │ XDP_PASS
                            ▼
  sk_buff allocated
       ↓
  ┌─────────────────────────────────────────────────┐
  │  TC ingress hook  (Cilium: pod policy ingress)  │
  │  Sees: sk_buff + socket + cgroup → pod identity │
  └─────────────────────────┬───────────────────────┘
                            ↓
  netfilter / IP routing
       ↓
  socket → process (syscall boundary)
  ┌─────────────────────────────────────────────────┐
  │  Tracepoint / kprobe  (Falco: syscall monitor)  │
  │  Sees: any kernel event, any process, any pod   │
  └─────────────────────────────────────────────────┘
  ┌─────────────────────────────────────────────────┐
  │  LSM hook  (Tetragon: kernel-level enforcement) │
  │  Sees: security check context. Can DENY.        │
  └─────────────────────────────────────────────────┘
       ↓
  IP routing → qdisc
  ┌─────────────────────────────────────────────────┐
  │  TC egress hook  (Cilium: pod policy egress)    │
  │  Sees: socket + cgroup on outbound traffic      │
  └─────────────────────────────────────────────────┘
       ↓
  NIC → wire
</code></pre>
<hr />
<p>eBPF program types define where in the kernel a hook fires and what it can see — and knowing the difference is what makes you effective when Cilium or Falco behave unexpectedly. What we hadn&#8217;t answered — and what a 2am incident eventually forced — is what kind of eBPF programs are actually running on your nodes, and why the difference matters when something breaks.</p>
<p>A pod in production was dropping roughly one in fifty outbound TCP connections. Not all of them — just enough to cause intermittent timeouts in the application logs. NetworkPolicy showed egress allowed. Cilium reported no violations. Running <code class="" data-line="">curl</code> manually from inside the pod worked every time.</p>
<p>I spent the better part of three hours eliminating possibilities. DNS. MTU. Node-level conntrack table exhaustion. Upstream firewall rules. Nothing.</p>
<p>Eventually, almost as an afterthought, I ran this:</p>
<pre><code class="" data-line="">sudo bpftool prog list
</code></pre>
<p>There were two TC programs attached to that pod&#8217;s veth interface. One from the current Cilium version. One from the previous version — left behind by a rolling upgrade that hadn&#8217;t cleaned up properly. Two programs. Different policy state. One was occasionally dropping packets based on rules that no longer existed in the current policy model.</p>
<p>The answer had been sitting in the kernel the whole time. I just didn&#8217;t know where to look.</p>
<p>That incident forced me to actually understand something I&#8217;d been hand-waving for two years: eBPF isn&#8217;t a single hook. It&#8217;s a family of program types, each attached to a different location in the kernel, each seeing different data, each suited for different problems. Understanding the difference is what separates &#8220;I run Cilium and Falco&#8221; from &#8220;I understand what Cilium and Falco are actually doing on my nodes&#8221; — and that difference matters when something breaks at 2am.</p>
<h2 id="the-command-you-should-run-on-your-cluster-right-now">The Command You Should Run on Your Cluster Right Now</h2>
<p>Before getting into the theory, do this:</p>
<pre><code class="" data-line=""># See every eBPF program loaded on the node
sudo bpftool prog list

# See every eBPF program attached to a network interface
sudo bpftool net list
</code></pre>
<p>On a node running Cilium and Falco, you&#8217;ll see something like this:</p>
<pre><code class="" data-line="">42: xdp           name cil_xdp_entry       loaded_at 2026-04-01T09:23:41
43: sched_cls     name cil_from_netdev      loaded_at 2026-04-01T09:23:41
44: sched_cls     name cil_to_netdev        loaded_at 2026-04-01T09:23:41
51: cgroup_sock_addr  name cil_sock4_connect loaded_at 2026-04-01T09:23:41
88: raw_tracepoint  name sys_enter          loaded_at 2026-04-01T09:23:55
89: raw_tracepoint  name sys_exit           loaded_at 2026-04-01T09:23:55
</code></pre>
<p>Each line is a different program type. Each one fires at a different point in the kernel. The type column — <code class="" data-line="">xdp</code>, <code class="" data-line="">sched_cls</code>, <code class="" data-line="">raw_tracepoint</code>, <code class="" data-line="">cgroup_sock_addr</code> — tells you where in the kernel execution path that program is attached and therefore what it can and cannot see.</p>
<p>If you see more programs than you expect on a specific interface — like I did — that&#8217;s your first clue.</p>
<h2 id="why-program-types-exist">Why Program Types Exist</h2>
<p>The Linux kernel isn&#8217;t a single pipeline. Network packets, system calls, file operations, process scheduling — these all run through different subsystems with different execution contexts and different available data.</p>
<p>eBPF lets you attach programs to specific points within those subsystems. The &#8220;program type&#8221; is the contract: it defines where the hook fires, what data the program receives, and what it&#8217;s allowed to do with it. A program designed to process network packets before they hit the kernel stack looks completely different from one designed to intercept system calls across all containers simultaneously.</p>
<p>Most of us will interact with four or five program types through the tools we already run. Understanding what each one actually is — where it sits, what it sees — is what makes you effective when those tools behave unexpectedly.</p>
<h2 id="the-types-behind-the-tools-you-already-use">The Types Behind the Tools You Already Use</h2>
<h3 id="tc-why-cilium-can-tell-which-pod-sent-a-packet">TC — Why Cilium Can Tell Which Pod Sent a Packet</h3>
<p>TC stands for Traffic Control. It&#8217;s where Cilium enforces your NetworkPolicy, and it&#8217;s what caused my incident.</p>
<p>TC programs attach to network interfaces — specifically to the ingress and egress directions of the pod&#8217;s virtual interface (<code class="" data-line="">lxcXXXXX</code> in Cilium&#8217;s naming). They fire after the kernel has already processed the packet enough to know its context: which socket created it, which cgroup that socket belongs to. Cgroup maps to container, container maps to pod.</p>
<p>This is the critical piece: <strong>TC is how Cilium knows which pod a packet belongs to</strong>. Without that cgroup context, per-pod policy enforcement isn&#8217;t possible.</p>
<pre><code class="" data-line=""># See TC programs on a pod&#039;s veth interface
sudo tc filter show dev lxc12345 ingress
sudo tc filter show dev lxc12345 egress

# If you see two entries on the same direction — that&#039;s the incident I described
# The priority number (pref 1, pref 2) tells you the order they run
</code></pre>
<p>When there are two TC programs on the same interface, the first one to return &#8220;drop&#8221; wins. The second program never runs. This is why the issue was intermittent rather than consistent — the stale program only matched specific connection patterns.</p>
<p>Fixing it is straightforward once you know what to look for:</p>
<pre><code class="" data-line=""># Remove a stale TC filter by its priority number
sudo tc filter del dev lxc12345 egress pref 2
</code></pre>
<p>Add this check to your post-upgrade runbook. Cilium upgrades are generally clean but not always.</p>
<h3 id="xdp-why-cilium-doesnt-use-tc-for-everything">XDP — Why Cilium Doesn&#8217;t Use TC for Everything</h3>
<p>If TC is good enough for pod-level policy, why does Cilium also run an XDP program on the node&#8217;s main interface? Look at the <code class="" data-line="">bpftool prog list</code> output again — there&#8217;s an <code class="" data-line="">xdp</code> program loaded alongside the TC programs.</p>
<p>XDP fires earlier. Much earlier. Before the kernel allocates any memory for the packet. Before routing. Before connection tracking. Before anything.</p>
<p>The tradeoff is exactly what you&#8217;d expect: XDP is fast but context-poor. It sees raw packet bytes. It doesn&#8217;t know which pod the packet came from. It can&#8217;t read cgroup information because no socket buffer has been allocated yet.</p>
<p>Cilium uses XDP specifically for ClusterIP service load balancing — when a packet arrives at the node destined for a service VIP, XDP rewrites the destination to the actual pod IP in a single map lookup and sends it on its way. No iptables. No conntrack. The work is done before the kernel stack is involved.</p>
<p>There&#8217;s a silent failure mode worth knowing about here. XDP runs in one of two modes:</p>
<ul>
<li><strong>Native mode</strong> — runs inside the NIC driver itself, before any kernel allocation. This is where the performance comes from.</li>
<li><strong>Generic mode</strong> — fallback when the NIC driver doesn&#8217;t support XDP. Runs later, after <code class="" data-line="">sk_buff</code> allocation. No performance benefit over iptables.</li>
</ul>
<p>If your NIC doesn&#8217;t support native XDP, Cilium silently falls back to generic mode. The policy still works — but the performance characteristics you assumed aren&#8217;t there.</p>
<pre><code class="" data-line=""># Check which XDP mode is active on your node&#039;s main interface
ip link show eth0 | grep xdp
# xdpdrv  ← native mode (fast)
# xdpgeneric ← generic mode (no perf benefit)
</code></pre>
<p>Most cloud provider instance types with modern Mellanox/Intel NICs support native mode. Worth verifying rather than assuming.</p>
<h3 id="tracepoints-how-falco-sees-every-container">Tracepoints — How Falco Sees Every Container</h3>
<p>Falco loads two programs: <code class="" data-line="">sys_enter</code> and <code class="" data-line="">sys_exit</code>. These are raw tracepoints — they fire on every single system call, from every process, in every container on the node.</p>
<p>Tracepoints are explicitly defined and maintained instrumentation points in the kernel. Unlike hooks that attach to specific internal function names (which can be renamed or inlined between kernel versions), tracepoints are stable interfaces. They&#8217;re part of the kernel&#8217;s public contract with tooling that wants to instrument it.</p>
<p>This matters operationally. When you patch your nodes — and cloud-managed nodes get patched frequently — tools built on tracepoints keep working. Tools built on kprobes (internal function hooks) may silently stop firing if the function they&#8217;re attached to gets renamed or inlined by the compiler in a new kernel build.</p>
<pre><code class="" data-line=""># Verify what Falco is actually using
sudo bpftool prog list | grep -E &quot;kprobe|tracepoint&quot;

# Falco&#039;s current eBPF driver should show raw_tracepoint entries
# If you see kprobe entries from Falco, you&#039;re on the older driver
# Check: falco --version and the driver being loaded at startup
</code></pre>
<p>If you&#8217;re running Falco on a cluster that gets regular OS patch upgrades and you haven&#8217;t verified the driver mode, check it. The older kprobe-based driver has a real failure mode on certain kernel versions.</p>
<h3 id="lsm-how-tetragon-blocks-operations-at-the-kernel-level">LSM — How Tetragon Blocks Operations at the Kernel Level</h3>
<p>LSM hooks run at the kernel&#8217;s security decision points: file opens, socket connections, process execution, capability checks. The defining characteristic is that they can <em>deny</em> an operation. Return an error from an LSM hook and the kernel refuses the syscall before it completes.</p>
<p>This is qualitatively different from observability hooks. kprobes and tracepoints watch. LSM hooks enforce.</p>
<p>When you see Tetragon configured to kill a process attempting a privileged operation, or block a container from writing to a specific path, that&#8217;s an LSM hook making the decision inside the kernel — not a sidecar watching traffic, not an admission webhook running before pod creation, not a userspace agent trying to act fast enough. The enforcement is in the kernel itself.</p>
<pre><code class="" data-line=""># See if any LSM eBPF programs are active on the node
sudo bpftool prog list | grep lsm

# Verify LSM eBPF support on your kernel (required for Tetragon enforcement mode)
grep CONFIG_BPF_LSM /boot/config-$(uname -r)
# CONFIG_BPF_LSM=y   ← required
</code></pre>
<h2 id="the-practical-summary">The Practical Summary</h2>
<table>
<thead>
<tr>
<th>What&#8217;s happening on your node</th>
<th>Program type</th>
<th>Where to look</th>
</tr>
</thead>
<tbody>
<tr>
<td>Cilium service load balancing</td>
<td>XDP</td>
<td><code class="" data-line="">ip link show eth0 \| grep xdp</code></td>
</tr>
<tr>
<td>Cilium pod network policy</td>
<td>TC (<code class="" data-line="">sched_cls</code>)</td>
<td><code class="" data-line="">tc filter show dev lxcXXXX egress</code></td>
</tr>
<tr>
<td>Falco syscall monitoring</td>
<td>Tracepoint</td>
<td><code class="" data-line="">bpftool prog list \| grep tracepoint</code></td>
</tr>
<tr>
<td>Tetragon enforcement</td>
<td>LSM</td>
<td><code class="" data-line="">bpftool prog list \| grep lsm</code></td>
</tr>
<tr>
<td>Anything unexpected</td>
<td>All types</td>
<td><code class="" data-line="">bpftool prog list</code>, <code class="" data-line="">bpftool net list</code></td>
</tr>
</tbody>
</table>
<h2 id="the-incident-revisited">The Incident, Revisited</h2>
<p>Three hours of debugging. The answer was a stale TC program sitting at priority 2 on a pod&#8217;s veth interface, left behind by an incomplete Cilium upgrade.</p>
<pre><code class="" data-line=""># What I should have run first
sudo bpftool net list
sudo tc filter show dev lxc12345 egress
</code></pre>
<p>Two commands. Thirty seconds. If I&#8217;d known that TC programs can stack on the same interface, I&#8217;d have started there.</p>
<p>That&#8217;s the point of understanding program types — not to write eBPF programs yourself, but to know where to look when the tools you depend on don&#8217;t behave the way you expect. The programs are already there, running on your nodes right now. <code class="" data-line="">bpftool prog list</code> shows you all of them.</p>
<h2 id="key-takeaways">Key Takeaways</h2>
<ul>
<li><code class="" data-line="">bpftool prog list</code> and <code class="" data-line="">bpftool net list</code> show every eBPF program on a node — run these before anything else when debugging eBPF-based tool behavior</li>
<li>TC programs can stack on the same interface; stale programs from incomplete Cilium upgrades cause intermittent drops — check <code class="" data-line="">tc filter show</code> after every Cilium upgrade</li>
<li>XDP runs before the kernel stack — fastest hook, but no pod identity; Cilium uses it for service load balancing, not pod policy</li>
<li>XDP silently falls back to generic mode on unsupported NICs — verify with <code class="" data-line="">ip link show | grep xdp</code></li>
<li>Tracepoints are stable across kernel versions; kprobe-based tools may silently break after node OS patches — verify your Falco driver mode</li>
<li>LSM hooks enforce at the kernel level — this is what makes Tetragon&#8217;s enforcement mode fundamentally different from sidecar-based approaches</li>
</ul>
<h2 id="whats-next">What&#8217;s Next</h2>
<p>Every eBPF program fires, does its work, and exits — but the work always involves data.</p>
<p>Get EP05 in your inbox when it publishes → <a href="https://linuxcent.com/subscribe">linuxcent.com/subscribe</a> Counting connections. Tracking processes. Streaming events to a detection engine. In EP05, I&#8217;ll cover eBPF maps: the persistent data layer that connects kernel programs to the tools consuming their output. Understanding maps explains a class of production issues — and makes <code class="" data-line="">bpftool map dump</code> useful rather than cryptic.</p>
<p><a class="a2a_button_mastodon" href="https://www.addtoany.com/add_to/mastodon?linkurl=https%3A%2F%2Flinuxcent.com%2Febpf-program-types-kubernetes%2F&amp;linkname=eBPF%20Program%20Types%20%E2%80%94%20What%E2%80%99s%20Actually%20Running%20on%20Your%20Nodes" title="Mastodon" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_email" href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Flinuxcent.com%2Febpf-program-types-kubernetes%2F&amp;linkname=eBPF%20Program%20Types%20%E2%80%94%20What%E2%80%99s%20Actually%20Running%20on%20Your%20Nodes" title="Email" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_whatsapp" href="https://www.addtoany.com/add_to/whatsapp?linkurl=https%3A%2F%2Flinuxcent.com%2Febpf-program-types-kubernetes%2F&amp;linkname=eBPF%20Program%20Types%20%E2%80%94%20What%E2%80%99s%20Actually%20Running%20on%20Your%20Nodes" title="WhatsApp" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_reddit" href="https://www.addtoany.com/add_to/reddit?linkurl=https%3A%2F%2Flinuxcent.com%2Febpf-program-types-kubernetes%2F&amp;linkname=eBPF%20Program%20Types%20%E2%80%94%20What%E2%80%99s%20Actually%20Running%20on%20Your%20Nodes" title="Reddit" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_x" href="https://www.addtoany.com/add_to/x?linkurl=https%3A%2F%2Flinuxcent.com%2Febpf-program-types-kubernetes%2F&amp;linkname=eBPF%20Program%20Types%20%E2%80%94%20What%E2%80%99s%20Actually%20Running%20on%20Your%20Nodes" title="X" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Flinuxcent.com%2Febpf-program-types-kubernetes%2F&amp;linkname=eBPF%20Program%20Types%20%E2%80%94%20What%E2%80%99s%20Actually%20Running%20on%20Your%20Nodes" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_copy_link" href="https://www.addtoany.com/add_to/copy_link?linkurl=https%3A%2F%2Flinuxcent.com%2Febpf-program-types-kubernetes%2F&amp;linkname=eBPF%20Program%20Types%20%E2%80%94%20What%E2%80%99s%20Actually%20Running%20on%20Your%20Nodes" title="Copy Link" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Flinuxcent.com%2Febpf-program-types-kubernetes%2F&#038;title=eBPF%20Program%20Types%20%E2%80%94%20What%E2%80%99s%20Actually%20Running%20on%20Your%20Nodes" data-a2a-url="https://linuxcent.com/ebpf-program-types-kubernetes/" data-a2a-title="eBPF Program Types — What’s Actually Running on Your Nodes"></a></p><p>The post <a href="https://linuxcent.com/ebpf-program-types-kubernetes/">eBPF Program Types — What&#8217;s Actually Running on Your Nodes</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxcent.com/ebpf-program-types-kubernetes/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1450</post-id>	</item>
		<item>
		<title>BPF Verifier Explained: Why eBPF Is Safe for Production Kubernetes</title>
		<link>https://linuxcent.com/bpf-verifier-kubernetes-safety/</link>
					<comments>https://linuxcent.com/bpf-verifier-kubernetes-safety/#respond</comments>
		
		<dc:creator><![CDATA[Vamshi Krishna Santhapuri]]></dc:creator>
		<pubDate>Sun, 22 Mar 2026 18:12:34 +0000</pubDate>
				<category><![CDATA[eBPF]]></category>
		<category><![CDATA[BPF Verifier]]></category>
		<category><![CDATA[bpftool]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[SRE]]></category>
		<guid isPermaLink="false">https://linuxcent.com/?p=1424</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 10</span> <span class="rt-label rt-postfix">minutes</span></span>The BPF verifier makes eBPF safe to run in kernel space — learn what it checks, what programs it rejects, and why no eBPF program can crash your nodes.</p>
<p>The post <a href="https://linuxcent.com/bpf-verifier-kubernetes-safety/">BPF Verifier Explained: Why eBPF Is Safe for Production Kubernetes</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 10</span> <span class="rt-label rt-postfix">minutes</span></span><style>
pre{position:relative;background:#1e1e1e;color:#d4d4d4;
    padding:16px 16px 16px 20px;border-radius:6px;overflow-x:auto;
    font-family:'JetBrains Mono','Fira Code','Cascadia Code',Consolas,'Courier New',monospace;
    font-size:.88em;line-height:1.6;border-left:4px solid #555}
code{background:#f4f4f4;padding:2px 5px;border-radius:3px;font-size:.9em}
pre code{background:transparent;padding:0;color:inherit}
pre[data-lang="bash"],pre[data-lang="sh"],
pre[data-lang="shell"],pre[data-lang="zsh"]{border-left-color:#4ec9b0}
pre[data-lang="yaml"],pre[data-lang="json"],
pre[data-lang="toml"],pre[data-lang="xml"]{border-left-color:#569cd6}
pre[data-lang="python"],pre[data-lang="go"],pre[data-lang="rust"],
pre[data-lang="java"],pre[data-lang="c"],pre[data-lang="cpp"]{border-left-color:#c586c0}
pre[data-lang="text"],pre[data-lang="output"],
pre[data-lang="console"]{border-left-color:#888}
.lc-copy-btn{position:absolute;top:8px;right:8px;background:#2d2d2d;color:#ccc;
    border:1px solid #444;border-radius:4px;padding:3px 9px;font-size:.75em;
    font-family:system-ui,sans-serif;cursor:pointer;opacity:0;
    transition:opacity .15s,background .15s;line-height:1.6}
pre:hover .lc-copy-btn{opacity:1}
.lc-copy-btn:hover{background:#3a3a3a;color:#fff}
.lc-copy-btn.copied{color:#4ec9b0;border-color:#4ec9b0}
.lc-lang-badge{position:absolute;top:8px;left:20px;font-family:system-ui,sans-serif;
    font-size:.7em;color:#666;text-transform:uppercase;letter-spacing:.04em;
    line-height:1;pointer-events:none;opacity:0;transition:opacity .15s}
pre:hover .lc-lang-badge{opacity:1}
table{border-collapse:collapse;width:100%;margin:16px 0}
th,td{border:1px solid #ddd;padding:10px 14px;text-align:left}
th{background:#f0f0f0;font-weight:600}
tr:nth-child(even){background:#fafafa}
</style>
<p><script>
(function(){
  if(window.__lcCodeEnhanced)return;
  window.__lcCodeEnhanced=true;
  function enhance(){
    document.querySelectorAll('pre').forEach(function(pre){
      var code=pre.querySelector('code');
      var lang='';
      if(code){var m=(code.className||'').match(/language-(\S+)/);if(m)lang=m[1].toLowerCase();}
      if(lang)pre.setAttribute('data-lang',lang);
      if(lang){var badge=document.createElement('span');badge.className='lc-lang-badge';badge.textContent=lang;pre.insertBefore(badge,pre.firstChild);}
      var btn=document.createElement('button');
      btn.className='lc-copy-btn';btn.textContent='Copy';btn.setAttribute('aria-label','Copy code to clipboard');
      pre.appendChild(btn);
      btn.addEventListener('click',function(){
        var text=code?code.innerText:pre.innerText;
        if(navigator.clipboard&&window.isSecureContext){
          navigator.clipboard.writeText(text).then(function(){ok(btn);}).catch(function(){fb(text,btn);});
        }else{fb(text,btn);}
      });
    });
  }
  function ok(btn){btn.textContent='Copied!';btn.classList.add('copied');setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},2000);}
  function fb(text,btn){
    try{var ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;left:-9999px;top:-9999px;opacity:0';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);ok(btn);}
    catch(e){btn.textContent='✗ Failed';setTimeout(function(){btn.textContent='Copy';},2000);}
  }
  if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',enhance);}else{enhance();}
})();
</script><br />
<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 9</span> <span class="rt-label rt-postfix">minutes</span></span></p>
<p><!-- ============================================================
     LINUXCENT.COM — Episode 2
     Paste into: WordPress Classic editor → Text tab
     OR Gutenberg → Custom HTML block
     Style: Matches linuxcent.com native theme — no custom CSS needed
     Updated: Added comprehensive distro eBPF support table
     ============================================================ --></p>
<p><em>~2,400 words &middot; Reading time: 9 min &middot; Series: eBPF: From Kernel to Cloud, Episode 2 of 18</em></p>
<p>In <a href="https://linuxcent.com/what-is-ebpf-linux-kubernetes/">Episode 1</a>, we established what eBPF is and why it gives Linux admins and DevOps engineers kernel-level visibility without sidecars or code changes. The obvious follow-up question is the one every experienced engineer should ask before running anything in kernel space:</p>
<p><strong>Is it actually safe to run on production nodes?</strong></p>
<p>The answer is yes &mdash; and the reason is one specific component of the Linux kernel called the BPF verifier. This post explains what the verifier is, what it protects your cluster from, and why it changes the risk calculus for eBPF-based tools entirely.</p>
<hr />
<div id="ez-toc-container" class="ez-toc-v2_0_82_2 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction">
<div class="ez-toc-title-container">
<p class="ez-toc-title" style="cursor:inherit">Table of Contents</p>
<p><span class="ez-toc-title-toggle"><a href="#" class="ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle" aria-label="Toggle Table of Content"><span class="ez-toc-js-icon-con"><span class=""><span class="eztoc-hide" style="display:none;">Toggle</span><span class="ez-toc-icon-toggle-span"><svg style="fill: #999;color:#999" xmlns="http://www.w3.org/2000/svg" class="list-377408" width="20px" height="20px" viewBox="0 0 24 24" fill="none"><path d="M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z" fill="currentColor"></path></svg><svg style="fill: #999;color:#999" class="arrow-unsorted-368013" xmlns="http://www.w3.org/2000/svg" width="10px" height="10px" viewBox="0 0 24 24" version="1.2" baseProfile="tiny"><path d="M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z"/></svg></span></span></span></a></span></div>
<nav>
<ul class='ez-toc-list ez-toc-list-level-1 ' >
<li class='ez-toc-page-1 ez-toc-heading-level-2'><a class="ez-toc-link ez-toc-heading-1" href="https://linuxcent.com/bpf-verifier-kubernetes-safety/#The_Fear_That_Holds_Most_Teams_Back" >The Fear That Holds Most Teams Back</a></li>
<li class='ez-toc-page-1 ez-toc-heading-level-2'><a class="ez-toc-link ez-toc-heading-2" href="https://linuxcent.com/bpf-verifier-kubernetes-safety/#What_the_Verifier_Actually_Is" >What the Verifier Actually Is</a></li>
<li class='ez-toc-page-1 ez-toc-heading-level-2'><a class="ez-toc-link ez-toc-heading-3" href="https://linuxcent.com/bpf-verifier-kubernetes-safety/#What_the_Verifier_Protects_You_From" >What the Verifier Protects You From</a>
<ul class='ez-toc-list-level-3' >
<li class='ez-toc-heading-level-3'><a class="ez-toc-link ez-toc-heading-4" href="https://linuxcent.com/bpf-verifier-kubernetes-safety/#Infinite_loops" >Infinite loops</a></li>
<li class='ez-toc-page-1 ez-toc-heading-level-3'><a class="ez-toc-link ez-toc-heading-5" href="https://linuxcent.com/bpf-verifier-kubernetes-safety/#Memory_safety_violations" >Memory safety violations</a></li>
<li class='ez-toc-page-1 ez-toc-heading-level-3'><a class="ez-toc-link ez-toc-heading-6" href="https://linuxcent.com/bpf-verifier-kubernetes-safety/#Kernel_crashes" >Kernel crashes</a></li>
<li class='ez-toc-page-1 ez-toc-heading-level-3'><a class="ez-toc-link ez-toc-heading-7" href="https://linuxcent.com/bpf-verifier-kubernetes-safety/#Privilege_escalation_and_kernel_pointer_leaks" >Privilege escalation and kernel pointer leaks</a></li>
</ul>
</li>
<li class='ez-toc-page-1 ez-toc-heading-level-2'><a class="ez-toc-link ez-toc-heading-8" href="https://linuxcent.com/bpf-verifier-kubernetes-safety/#eBPF_vs_Traditional_Observability_Agents" >eBPF vs Traditional Observability Agents</a>
<ul class='ez-toc-list-level-3' >
<li class='ez-toc-heading-level-3'><a class="ez-toc-link ez-toc-heading-9" href="https://linuxcent.com/bpf-verifier-kubernetes-safety/#Traditional_agent_%E2%80%94_DaemonSet_sidecar_approach" >Traditional agent &mdash; DaemonSet sidecar approach</a></li>
<li class='ez-toc-page-1 ez-toc-heading-level-3'><a class="ez-toc-link ez-toc-heading-10" href="https://linuxcent.com/bpf-verifier-kubernetes-safety/#eBPF-based_tool_%E2%80%94_Cilium_Falco_Tetragon" >eBPF-based tool &mdash; Cilium / Falco / Tetragon</a></li>
</ul>
</li>
<li class='ez-toc-page-1 ez-toc-heading-level-2'><a class="ez-toc-link ez-toc-heading-11" href="https://linuxcent.com/bpf-verifier-kubernetes-safety/#Tools_You_Are_Probably_Already_Running_%E2%80%94_All_Verifier-Protected" >Tools You Are Probably Already Running &mdash; All Verifier-Protected</a></li>
<li class='ez-toc-page-1 ez-toc-heading-level-2'><a class="ez-toc-link ez-toc-heading-12" href="https://linuxcent.com/bpf-verifier-kubernetes-safety/#Questions_to_Ask_When_Evaluating_eBPF_Tools" >Questions to Ask When Evaluating eBPF Tools</a>
<ul class='ez-toc-list-level-3' >
<li class='ez-toc-heading-level-3'><a class="ez-toc-link ez-toc-heading-13" href="https://linuxcent.com/bpf-verifier-kubernetes-safety/#1_What_kernel_version_do_you_require" >1. What kernel version do you require?</a></li>
<li class='ez-toc-page-1 ez-toc-heading-level-3'><a class="ez-toc-link ez-toc-heading-14" href="https://linuxcent.com/bpf-verifier-kubernetes-safety/#2_Do_you_use_CO-RE" >2. Do you use CO-RE?</a></li>
<li class='ez-toc-page-1 ez-toc-heading-level-3'><a class="ez-toc-link ez-toc-heading-15" href="https://linuxcent.com/bpf-verifier-kubernetes-safety/#3_What_eBPF_program_types_do_you_use" >3. What eBPF program types do you use?</a></li>
</ul>
</li>
<li class='ez-toc-page-1 ez-toc-heading-level-2'><a class="ez-toc-link ez-toc-heading-16" href="https://linuxcent.com/bpf-verifier-kubernetes-safety/#How_Falco_Uses_the_Verifier_%E2%80%94_A_Step-by-Step_Walkthrough" >How Falco Uses the Verifier &mdash; A Step-by-Step Walkthrough</a></li>
<li class='ez-toc-page-1 ez-toc-heading-level-2'><a class="ez-toc-link ez-toc-heading-17" href="https://linuxcent.com/bpf-verifier-kubernetes-safety/#The_Bottom_Line" >The Bottom Line</a></li>
<li class='ez-toc-page-1 ez-toc-heading-level-2'><a class="ez-toc-link ez-toc-heading-18" href="https://linuxcent.com/bpf-verifier-kubernetes-safety/#Further_Reading" >Further Reading</a></li>
</ul>
</nav>
</div>
<p style="font-size:0.72em;font-weight:700;letter-spacing:0.12em;color:#f59e0b;text-transform:uppercase;margin:2em 0 0.75em 0;text-align:center;">Architecture Overview</p>
<figure class="wp-block-image size-full" style="margin:0 0 0.5em 0;">
<img decoding="async" width="1176" height="2560" src="https://linuxcent.com/wp-content/uploads/2026/05/ep02-bpf-verifier-og-2-scaled.png" alt="BPF Verifier and JIT pipeline — how eBPF programs are safety-checked and compiled before kernel execution" class="wp-image-2109" style="width:100%;height:auto;display:block;border-radius:8px;" srcset="https://linuxcent.com/wp-content/uploads/2026/05/ep02-bpf-verifier-og-2-scaled.png 1176w, https://linuxcent.com/wp-content/uploads/2026/05/ep02-bpf-verifier-og-2-138x300.png 138w, https://linuxcent.com/wp-content/uploads/2026/05/ep02-bpf-verifier-og-2-471x1024.png 471w, https://linuxcent.com/wp-content/uploads/2026/05/ep02-bpf-verifier-og-2-768x1671.png 768w, https://linuxcent.com/wp-content/uploads/2026/05/ep02-bpf-verifier-og-2-706x1536.png 706w, https://linuxcent.com/wp-content/uploads/2026/05/ep02-bpf-verifier-og-2-941x2048.png 941w" sizes="(max-width: 1176px) 100vw, 1176px" /><figcaption style="text-align:center;font-size:0.85em;color:#6b7280;margin-top:0.75em;">The BPF verifier runs before every eBPF load — rejecting unsafe programs before they touch the kernel.</figcaption></figure>
<hr style="border:none;border-top:1px solid #e5e7eb;margin:0.5em 0 2em 0;"/>
<h2 id="tldr">TL;DR</h2>
<ul>
<li>The BPF verifier is a static analysis pass that runs before every eBPF program loads — it rejects unsafe programs before they touch the kernel</li>
<li>It prevents infinite loops (only bounded loops allowed), out-of-bounds memory access, null pointer dereferences, and privilege escalation via kernel pointer leaks</li>
<li>Unlike kernel modules, a verified eBPF program cannot kernel-panic your node — that guarantee is why eBPF-based tools are safe in production</li>
<li>Every eBPF-based tool you run — Cilium, Falco, Tetragon, Datadog — passes its programs through the verifier on every node load</li>
<li>Ask three questions before adopting any eBPF tool: minimum kernel version required, CO-RE support (portable across kernels), and which program types it uses</li>
<li><em>(The verifier is also why eBPF programs require <code class="" data-line="">CAP_BPF</code> or <code class="" data-line="">CAP_SYS_ADMIN</code> — privilege is still required to load, just not to survive a bad load)</em></li>
</ul>
<hr />
<h2><span class="ez-toc-section" id="The_Fear_That_Holds_Most_Teams_Back"></span>The Fear That Holds Most Teams Back<span class="ez-toc-section-end"></span></h2>
<p>When I first explain eBPF to Linux admins and DevOps engineers, the reaction is almost always the same:</p>
<blockquote>
<p>&ldquo;So it runs code inside the kernel? On our production nodes? That sounds like a disaster waiting to happen.&rdquo;</p>
</blockquote>
<p>It is a completely reasonable concern. The Linux kernel is not a place where mistakes are tolerated. A buggy kernel module can take down a server instantly &mdash; no warning, no graceful shutdown, just a hard panic and a 3 AM phone call.</p>
<p>I know this from personal experience. During 2012&ndash;2014, I worked briefly with Linux device driver code. That period taught me one thing clearly: kernel space does not forgive careless code.</p>
<p>So when people started talking about running programs inside the kernel via eBPF, my instinct was scepticism too. Then I understood the BPF verifier. And everything changed.</p>
<hr />
<h2><span class="ez-toc-section" id="What_the_Verifier_Actually_Is"></span>What the Verifier Actually Is<span class="ez-toc-section-end"></span></h2>
<p>Think of the BPF verifier as a strict safety gate that sits between your eBPF program and the kernel. Before your eBPF program is allowed to run &mdash; before it touches a single system call, network packet, or container event &mdash; the verifier reads through every line of it and asks one question:</p>
<blockquote>
<p><strong>&ldquo;Could this program crash or compromise the kernel?&rdquo;</strong></p>
<p>If the answer is yes, or even <em>maybe</em>, the program is rejected. It does not load. Your cluster stays safe. If the answer is a provable no, the program loads and runs.</p>
</blockquote>
<p>This is not a runtime check that catches problems after the fact. It is a <strong>load-time guarantee</strong> &mdash; the kernel proves the program is safe before it ever executes. Here is what that looks like when you deploy Cilium:</p>
<pre><code class="" data-line="">You run: kubectl apply -f cilium-daemonset.yaml
         └─► Cilium loads its eBPF programs onto each node
                   └─► Kernel verifier checks every program
                             ├─► SAFE   → program loads, starts observing
                             └─► UNSAFE → rejected, cluster untouched</code></pre>
<p>This is why Cilium can replace kube-proxy on your nodes, why Falco can watch every syscall in every container, and why Tetragon can enforce security policy at the kernel level &mdash; all without putting your cluster at risk.</p>
<hr />
<h2><span class="ez-toc-section" id="What_the_Verifier_Protects_You_From"></span>What the Verifier Protects You From<span class="ez-toc-section-end"></span></h2>
<p>You do not need to know how the verifier works internally. What matters is what it prevents &mdash; and why each protection matters specifically in Kubernetes environments.</p>
<h3><span class="ez-toc-section" id="Infinite_loops"></span>Infinite loops<span class="ez-toc-section-end"></span></h3>
<p>An eBPF program that never terminates would freeze the kernel event it is attached to &mdash; potentially hanging every container on that node. The verifier rejects any program it cannot prove will finish executing within a bounded number of instructions.</p>
<p><strong>Why this matters:</strong> Every eBPF-based tool on your K8s nodes &mdash; Cilium, Falco, Tetragon, Hubble &mdash; was verified to terminate correctly on every code path before it shipped. You are not trusting the vendor&rsquo;s claim. The kernel enforced it.</p>
<h3><span class="ez-toc-section" id="Memory_safety_violations"></span>Memory safety violations<span class="ez-toc-section-end"></span></h3>
<p>An eBPF program cannot read or write memory outside the boundaries it is explicitly granted. No reaching into another container&rsquo;s memory space. No accessing kernel data structures it was not given permission to touch.</p>
<p><strong>Why this matters:</strong> This is the property that makes eBPF safe for multi-tenant clusters. A Falco rule monitoring one namespace cannot accidentally read data from another namespace&rsquo;s containers. The verifier makes this impossible at the program level, not just at the policy level.</p>
<h3><span class="ez-toc-section" id="Kernel_crashes"></span>Kernel crashes<span class="ez-toc-section-end"></span></h3>
<p>The verifier checks that every pointer is valid before it is dereferenced, that every function call uses correct arguments, and that the program cannot corrupt kernel data structures. Programs that could cause a kernel panic are rejected before they load.</p>
<p><strong>Why this matters:</strong> Running Cilium or Tetragon on a production node is not the same risk as loading an untested kernel module. The verifier has already proven these programs cannot crash your nodes &mdash; before they ever ran on your infrastructure.</p>
<h3><span class="ez-toc-section" id="Privilege_escalation_and_kernel_pointer_leaks"></span>Privilege escalation and kernel pointer leaks<span class="ez-toc-section-end"></span></h3>
<p>eBPF programs cannot leak kernel memory addresses to userspace. This closes a class of container escape and privilege escalation attacks that have historically been possible through kernel module vulnerabilities.</p>
<p><strong>Why this matters:</strong> Security tools built on eBPF &mdash; like Tetragon, which detects and blocks container escape attempts in real time &mdash; are not themselves a vector for the attacks they protect against.</p>
<hr />
<h2><span class="ez-toc-section" id="eBPF_vs_Traditional_Observability_Agents"></span>eBPF vs Traditional Observability Agents<span class="ez-toc-section-end"></span></h2>
<p>To appreciate what the verifier gives you operationally, compare the two main approaches to K8s observability.</p>
<h3><span class="ez-toc-section" id="Traditional_agent_%E2%80%94_DaemonSet_sidecar_approach"></span>Traditional agent &mdash; DaemonSet sidecar approach<span class="ez-toc-section-end"></span></h3>
<pre><code class="" data-line="">Your K8s cluster
└─► Node
    ├─► App Pod (your service)
    ├─► Sidecar container (injected into every pod)
    │   └─► Reads /proc, intercepts syscalls via ptrace
    │       └─► 15–30% CPU/memory overhead per pod
    └─► Agent DaemonSet Pod
        └─► Aggregates data from all sidecars</code></pre>
<p>Problems with this model:</p>
<ul>
<li>Sidecar injection requires modifying every pod spec and typically an admission webhook</li>
<li>ptrace-based interception adds 50&ndash;100% overhead to the traced process and is blocked in hardened containers</li>
<li>The agent runs in userspace with elevated privileges &mdash; a larger attack surface</li>
<li>Updating the agent requires pod restarts across your fleet</li>
</ul>
<h3><span class="ez-toc-section" id="eBPF-based_tool_%E2%80%94_Cilium_Falco_Tetragon"></span>eBPF-based tool &mdash; Cilium / Falco / Tetragon<span class="ez-toc-section-end"></span></h3>
<pre><code class="" data-line="">Your K8s cluster
└─► Node
    ├─► App Pod (your service — completely unmodified)
    ├─► App Pod (another service — also unmodified)
    └─► eBPF programs (inside the kernel, verifier-checked)
        └─► See every syscall, network packet, file access
            └─► Forward events to userspace agent via ring buffer</code></pre>
<p>Benefits:</p>
<ul>
<li>No sidecar injection &mdash; pod specs stay clean, no admission webhook required</li>
<li>Kernel-level visibility with near-zero overhead (typically 1&ndash;3%)</li>
<li>The verifier guarantees the eBPF programs cannot harm your nodes</li>
<li>Works identically with Docker, containerd, and CRI-O</li>
</ul>
<hr />
<h2><span class="ez-toc-section" id="Tools_You_Are_Probably_Already_Running_%E2%80%94_All_Verifier-Protected"></span>Tools You Are Probably Already Running &mdash; All Verifier-Protected<span class="ez-toc-section-end"></span></h2>
<p>You may already be running eBPF on your nodes without thinking about it explicitly. In each case below, the verifier ran before the tool ever touched your cluster.</p>
<table>
<thead>
<tr>
<th>Tool</th>
<th>How the verifier is involved</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Cilium</strong></td>
<td>Every network policy decision, service load-balancing operation, and Hubble flow log is handled by eBPF programs that passed the verifier at node startup.</td>
</tr>
<tr>
<td><strong>Falco</strong></td>
<td>Every Falco rule is enforced by a verifier-checked eBPF program attached to syscall hooks. Sub-millisecond detection is only possible because the program runs in kernel space.</td>
</tr>
<tr>
<td><strong>AWS VPC CNI</strong></td>
<td>On EKS, networking operations have progressively moved to eBPF for performance at scale. If you are on a recent EKS AMI, eBPF is already doing work on your nodes.</td>
</tr>
<tr>
<td><strong>systemd</strong></td>
<td>Modern systemd uses eBPF for cgroup-based resource accounting and network traffic control. Active on most current Ubuntu, RHEL, and Amazon Linux 2023 installations.</td>
</tr>
</tbody>
</table>
<hr />
<h2><span class="ez-toc-section" id="Questions_to_Ask_When_Evaluating_eBPF_Tools"></span>Questions to Ask When Evaluating eBPF Tools<span class="ez-toc-section-end"></span></h2>
<p>When a vendor tells you their tool uses eBPF, these three questions will quickly tell you how mature their implementation is.</p>
<h3><span class="ez-toc-section" id="1_What_kernel_version_do_you_require"></span>1. What kernel version do you require?<span class="ez-toc-section-end"></span></h3>
<p>The verifier&rsquo;s capabilities have expanded significantly across kernel versions. Tools targeting kernel 5.8+ can use more powerful features safely. Tools claiming to work on kernel 4.x are constrained by an older, more limited verifier. The table below shows exactly where each major distribution stands.</p>
<table>
<thead>
<tr>
<th>Distribution</th>
<th>Default kernel</th>
<th>eBPF support level</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Ubuntu 16.04 LTS</strong></td>
<td>4.4</td>
<td>Basic eBPF only</td>
<td>No BTF. kprobes and socket filters work but modern tooling like Cilium and Falco eBPF driver will not run. EOL &mdash; do not use for new deployments.</td>
</tr>
<tr>
<td><strong>Ubuntu 18.04 LTS</strong></td>
<td>4.15</td>
<td>eBPF, no BTF</td>
<td>No CO-RE. Tools must be compiled against the exact running kernel headers. The HWE kernel (5.4) improves this but BTF still varies by build.</td>
</tr>
<tr>
<td><strong>Ubuntu 20.04 LTS</strong></td>
<td>5.4</td>
<td>BTF available, verify before use</td>
<td>CO-RE capable on most deployments. <code class="" data-line="">CONFIG_DEBUG_INFO_BTF</code> was absent on some early builds. Verify with <code class="" data-line="">ls /sys/kernel/btf/vmlinux</code> before deploying eBPF tooling. Cloud images generally have it enabled.</td>
</tr>
<tr>
<td><strong>Ubuntu 20.10+</strong></td>
<td>5.8</td>
<td>Full BTF + CO-RE</td>
<td>First Ubuntu release where BTF was consistently enabled by default. Ring buffers available. Not an LTS release &mdash; use 22.04 for production.</td>
</tr>
<tr>
<td><strong>Ubuntu 22.04 LTS</strong></td>
<td>5.15</td>
<td>Full modern eBPF &mdash; production ready</td>
<td>BTF embedded. Ring buffers, global variables, LSM hooks. Default baseline for EKS-optimised Ubuntu AMIs. Recommended for new deployments.</td>
</tr>
<tr>
<td><strong>Ubuntu 24.04 LTS</strong></td>
<td>6.8</td>
<td>Full modern eBPF + latest features</td>
<td>Open-coded iterators, improved verifier precision, enhanced LSM support. Best Ubuntu option for cutting-edge eBPF tooling today.</td>
</tr>
<tr>
<td><strong>Debian 10 (Buster)</strong></td>
<td>4.19</td>
<td>Basic eBPF, no BTF</td>
<td>eBPF programs load but CO-RE is unavailable. Must compile against exact kernel headers. EOL &mdash; migrate to Debian 11 or 12.</td>
</tr>
<tr>
<td><strong>Debian 11 (Bullseye)</strong></td>
<td>5.10 LTS</td>
<td>Full BTF + CO-RE</td>
<td>BTF enabled. CO-RE works. Cilium, Falco, and Tetragon all fully supported. Solid production baseline for Debian environments through 2026.</td>
</tr>
<tr>
<td><strong>Debian 12 (Bookworm)</strong></td>
<td>6.1 LTS</td>
<td>Full modern eBPF &mdash; production ready</td>
<td>Same kernel generation as Amazon Linux 2023. LSM hooks, ring buffers, full CO-RE. Recommended Debian version for eBPF workloads today.</td>
</tr>
<tr>
<td><strong>Debian 13 (Trixie)</strong></td>
<td>6.12 LTS</td>
<td>Full modern eBPF + latest features</td>
<td>Released August 2025. Same kernel generation as RHEL 10 / Rocky 10 / AlmaLinux 10. Maximum eBPF feature availability across all program types.</td>
</tr>
<tr>
<td><strong>RHEL 7.6</strong></td>
<td>3.10 (backported)</td>
<td>Tech Preview only &mdash; not production safe</td>
<td>First RHEL release to enable eBPF but explicitly marked as Tech Preview. Limited to kprobes and tracepoints. No XDP, no socket filters, no BTF. Do not use for eBPF in production.</td>
</tr>
<tr>
<td><strong>RHEL 8 / Rocky 8 / AlmaLinux 8</strong></td>
<td>4.18 (heavily backported)</td>
<td>Full BPF + BTF &mdash; functionally 5.4-equivalent</td>
<td>Red Hat backports make RHEL 8 kernels functionally comparable to upstream 5.4 for most eBPF use cases. BTF enabled across all releases. CO-RE works. Cilium treats RHEL 8.6+ as its minimum supported RHEL-family version.</td>
</tr>
<tr>
<td><strong>RHEL 9 / Rocky 9 / AlmaLinux 9</strong></td>
<td>5.14 (heavily backported)</td>
<td>Full modern eBPF &mdash; production ready</td>
<td>BTF embedded. XDP, tc, kprobe, tracepoint, and LSM hooks all supported. Falco, Cilium, and Tetragon fully supported. <strong>Recommended RHEL-family version for eBPF deployments today.</strong> Supported until 2032.</td>
</tr>
<tr>
<td><strong>RHEL 10 / Rocky 10 / AlmaLinux 10</strong></td>
<td>6.12</td>
<td>Full modern eBPF + latest features</td>
<td>Same kernel generation as Debian 13 and upstream 6.12 LTS. Rocky 10 released June 2025, AlmaLinux 10 released May 2025. Enhanced eBPF functionality throughout.</td>
</tr>
<tr>
<td><strong>Amazon Linux 2023</strong></td>
<td>6.1+</td>
<td>Full modern eBPF &mdash; production ready</td>
<td>BTF embedded. Full CO-RE. Recommended for EKS. Also resolves the NetworkManager deprecation issues in EKS 1.33+ &mdash; see the <a href="https://linuxcent.com/eks-1-33-networkmanager-systemd-networkd-migration-fix/">EKS 1.33 post</a>.</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>Quick check for any distro:</strong> Run <code class="" data-line="">ls /sys/kernel/btf/vmlinux</code> on your node. If the file exists, your kernel has BTF enabled and CO-RE-based eBPF tools will work correctly. If it does not exist, you are limited to tools that compile against your specific kernel headers. Run <code class="" data-line="">uname -r</code> to confirm the exact kernel version.</p>
</blockquote>
<blockquote>
<p><strong>Rocky Linux and AlmaLinux note:</strong> Both distros rebuild directly from RHEL sources. Their kernel versions and eBPF capabilities are effectively identical to the corresponding RHEL release. When Cilium or Falco document &ldquo;RHEL 9 support&rdquo;, that applies equally to Rocky 9 and AlmaLinux 9 without any additional configuration.</p>
</blockquote>
<h3><span class="ez-toc-section" id="2_Do_you_use_CO-RE"></span>2. Do you use CO-RE?<span class="ez-toc-section-end"></span></h3>
<p>CO-RE (Compile Once, Run Everywhere) means the tool&rsquo;s eBPF programs work correctly across different kernel versions without recompilation. Tools using CO-RE are more portable and significantly less likely to break after a routine node OS update. This is a reliable signal of engineering maturity in the vendor&rsquo;s eBPF implementation.</p>
<h3><span class="ez-toc-section" id="3_What_eBPF_program_types_do_you_use"></span>3. What eBPF program types do you use?<span class="ez-toc-section-end"></span></h3>
<p>Different program types have different privilege levels and access scopes. A tool that only needs <code class="" data-line="">kprobe</code> access is asking for considerably less privilege than one requiring <code class="" data-line="">lsm</code> hooks.</p>
<ul>
<li><code class="" data-line="">kprobe</code> / <code class="" data-line="">tracepoint</code> &mdash; observability and debugging</li>
<li><code class="" data-line="">tc</code> (traffic control) &mdash; network policy enforcement</li>
<li><code class="" data-line="">xdp</code> (eXpress Data Path) &mdash; high-performance packet processing</li>
<li><code class="" data-line="">lsm</code> (Linux Security Module) &mdash; security policy enforcement (used by Tetragon)</li>
</ul>
<p>Understanding the program type tells you what the tool can and cannot see on your nodes, and how much kernel access you are granting it.</p>
<hr />
<h2><span class="ez-toc-section" id="How_Falco_Uses_the_Verifier_%E2%80%94_A_Step-by-Step_Walkthrough"></span>How Falco Uses the Verifier &mdash; A Step-by-Step Walkthrough<span class="ez-toc-section-end"></span></h2>
<p>Here is exactly what happens when Falco starts on one of your K8s nodes, and where the verifier fits in:</p>
<pre><code class="" data-line="">1. Falco pod starts on the node (via DaemonSet)

2. Falco loads its eBPF programs into the kernel:
   └─► BPF verifier checks each program
       ├─► Can it crash the kernel?            No → continue
       ├─► Can it loop forever?                No → continue
       ├─► Can it access out-of-bounds memory? No → continue
       └─► PASS → program loads

3. Falco&#039;s eBPF programs attach to syscall hooks:
   └─► sys_enter_execve   (every process execution in every container)
   └─► sys_enter_openat   (every file open)
   └─► sys_enter_connect  (every outbound network connection)

4. A container runs an unexpected shell (potential attack):
   └─► execve() called inside the container
   └─► Falco&#039;s eBPF hook fires in kernel space
   └─► Event forwarded to Falco userspace via ring buffer
   └─► Falco rule matches: &quot;shell spawned in container&quot;
   └─► Alert fired in under 1 millisecond

5. Your container, your other pods, your node: completely unaffected</code></pre>
<blockquote>
<p>Step 2 is what the verifier makes safe. Without it, attaching eBPF hooks to every syscall on your production node would be an unacceptable risk. With it, Falco can offer this level of visibility with a mathematical safety guarantee.</p>
</blockquote>
<hr />
<h2><span class="ez-toc-section" id="The_Bottom_Line"></span>The Bottom Line<span class="ez-toc-section-end"></span></h2>
<p>You do not need to understand BPF bytecode, register states, or static analysis to use eBPF tools safely in production. What you do need to understand is this:</p>
<blockquote>
<p>The BPF verifier is the reason eBPF is fundamentally different from kernel modules. It does not just make eBPF &ldquo;safer&rdquo; in a vague sense &mdash; it provides a mathematical proof that each program cannot crash your kernel before that program ever runs.</p>
</blockquote>
<p>This is why eBPF-based tools can deliver deep kernel-level visibility into every container, every syscall, and every network flow &mdash; with near-zero overhead, no sidecar injection, and production safety that kernel modules could never guarantee.</p>
<p>The next time someone on your team hesitates about running Cilium, Falco, or Tetragon on production nodes because <em>&ldquo;it runs code in the kernel&rdquo;</em> &mdash; you now know what to tell them. The verifier already checked it. Before it ever touched your cluster.</p>
<hr />
<h2><span class="ez-toc-section" id="Further_Reading"></span>Further Reading<span class="ez-toc-section-end"></span></h2>
<ul>
<li><a href="https://docs.cilium.io/en/stable/concepts/ebpf/" target="_blank" rel="noopener noreferrer">Cilium documentation: eBPF and the Linux kernel</a></li>
<li><a href="https://falco.org/docs/event-sources/kernel/ebpf/" target="_blank" rel="noopener noreferrer">Falco: eBPF probe documentation</a></li>
<li><a href="https://tetragon.io/docs/" target="_blank" rel="noopener noreferrer">Tetragon: eBPF-based security observability</a></li>
<li><a href="https://ebpf.io" target="_blank" rel="noopener noreferrer">The official eBPF website: ebpf.io</a></li>
</ul>
<hr />
<p><em>Questions or corrections? Reach me on <a href="https://www.linkedin.com/in/vamshikrishnasanthapuri/" target="_blank" rel="noopener noreferrer">LinkedIn</a>. If this was useful, the full series index is on <a href="https://linuxcent.com">linuxcent.com</a> &mdash; search the <strong>eBPF Series</strong> tag for all episodes.</em></p>
<p><a class="a2a_button_mastodon" href="https://www.addtoany.com/add_to/mastodon?linkurl=https%3A%2F%2Flinuxcent.com%2Fbpf-verifier-kubernetes-safety%2F&amp;linkname=BPF%20Verifier%20Explained%3A%20Why%20eBPF%20Is%20Safe%20for%20Production%20Kubernetes" title="Mastodon" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_email" href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Flinuxcent.com%2Fbpf-verifier-kubernetes-safety%2F&amp;linkname=BPF%20Verifier%20Explained%3A%20Why%20eBPF%20Is%20Safe%20for%20Production%20Kubernetes" title="Email" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_whatsapp" href="https://www.addtoany.com/add_to/whatsapp?linkurl=https%3A%2F%2Flinuxcent.com%2Fbpf-verifier-kubernetes-safety%2F&amp;linkname=BPF%20Verifier%20Explained%3A%20Why%20eBPF%20Is%20Safe%20for%20Production%20Kubernetes" title="WhatsApp" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_reddit" href="https://www.addtoany.com/add_to/reddit?linkurl=https%3A%2F%2Flinuxcent.com%2Fbpf-verifier-kubernetes-safety%2F&amp;linkname=BPF%20Verifier%20Explained%3A%20Why%20eBPF%20Is%20Safe%20for%20Production%20Kubernetes" title="Reddit" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_x" href="https://www.addtoany.com/add_to/x?linkurl=https%3A%2F%2Flinuxcent.com%2Fbpf-verifier-kubernetes-safety%2F&amp;linkname=BPF%20Verifier%20Explained%3A%20Why%20eBPF%20Is%20Safe%20for%20Production%20Kubernetes" title="X" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Flinuxcent.com%2Fbpf-verifier-kubernetes-safety%2F&amp;linkname=BPF%20Verifier%20Explained%3A%20Why%20eBPF%20Is%20Safe%20for%20Production%20Kubernetes" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_copy_link" href="https://www.addtoany.com/add_to/copy_link?linkurl=https%3A%2F%2Flinuxcent.com%2Fbpf-verifier-kubernetes-safety%2F&amp;linkname=BPF%20Verifier%20Explained%3A%20Why%20eBPF%20Is%20Safe%20for%20Production%20Kubernetes" title="Copy Link" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Flinuxcent.com%2Fbpf-verifier-kubernetes-safety%2F&#038;title=BPF%20Verifier%20Explained%3A%20Why%20eBPF%20Is%20Safe%20for%20Production%20Kubernetes" data-a2a-url="https://linuxcent.com/bpf-verifier-kubernetes-safety/" data-a2a-title="BPF Verifier Explained: Why eBPF Is Safe for Production Kubernetes"></a></p><p>The post <a href="https://linuxcent.com/bpf-verifier-kubernetes-safety/">BPF Verifier Explained: Why eBPF Is Safe for Production Kubernetes</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxcent.com/bpf-verifier-kubernetes-safety/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1424</post-id>	</item>
		<item>
		<title>What Is eBPF? A Plain-English Guide for Linux and Kubernetes Engineers</title>
		<link>https://linuxcent.com/what-is-ebpf-linux-kubernetes/</link>
					<comments>https://linuxcent.com/what-is-ebpf-linux-kubernetes/#respond</comments>
		
		<dc:creator><![CDATA[Vamshi Krishna Santhapuri]]></dc:creator>
		<pubDate>Thu, 19 Mar 2026 17:53:41 +0000</pubDate>
				<category><![CDATA[eBPF]]></category>
		<category><![CDATA[bpftool]]></category>
		<category><![CDATA[Cilium]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Observability]]></category>
		<category><![CDATA[SRE]]></category>
		<guid isPermaLink="false">https://linuxcent.com/?p=1429</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 7</span> <span class="rt-label rt-postfix">minutes</span></span>What is eBPF? A sandboxed way to run programs in the Linux kernel — the technology behind Cilium, Falco, and Tetragon on your Kubernetes nodes.</p>
<p>The post <a href="https://linuxcent.com/what-is-ebpf-linux-kubernetes/">What Is eBPF? A Plain-English Guide for Linux and Kubernetes Engineers</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 7</span> <span class="rt-label rt-postfix">minutes</span></span><style>
pre{position:relative;background:#1e1e1e;color:#d4d4d4;
    padding:16px 16px 16px 20px;border-radius:6px;overflow-x:auto;
    font-family:'JetBrains Mono','Fira Code','Cascadia Code',Consolas,'Courier New',monospace;
    font-size:.88em;line-height:1.6;border-left:4px solid #555}
code{background:#f4f4f4;padding:2px 5px;border-radius:3px;font-size:.9em}
pre code{background:transparent;padding:0;color:inherit}
pre[data-lang="bash"],pre[data-lang="sh"],
pre[data-lang="shell"],pre[data-lang="zsh"]{border-left-color:#4ec9b0}
pre[data-lang="yaml"],pre[data-lang="json"],
pre[data-lang="toml"],pre[data-lang="xml"]{border-left-color:#569cd6}
pre[data-lang="python"],pre[data-lang="go"],pre[data-lang="rust"],
pre[data-lang="java"],pre[data-lang="c"],pre[data-lang="cpp"]{border-left-color:#c586c0}
pre[data-lang="text"],pre[data-lang="output"],
pre[data-lang="console"]{border-left-color:#888}
.lc-copy-btn{position:absolute;top:8px;right:8px;background:#2d2d2d;color:#ccc;
    border:1px solid #444;border-radius:4px;padding:3px 9px;font-size:.75em;
    font-family:system-ui,sans-serif;cursor:pointer;opacity:0;
    transition:opacity .15s,background .15s;line-height:1.6}
pre:hover .lc-copy-btn{opacity:1}
.lc-copy-btn:hover{background:#3a3a3a;color:#fff}
.lc-copy-btn.copied{color:#4ec9b0;border-color:#4ec9b0}
.lc-lang-badge{position:absolute;top:8px;left:20px;font-family:system-ui,sans-serif;
    font-size:.7em;color:#666;text-transform:uppercase;letter-spacing:.04em;
    line-height:1;pointer-events:none;opacity:0;transition:opacity .15s}
pre:hover .lc-lang-badge{opacity:1}
table{border-collapse:collapse;width:100%;margin:16px 0}
th,td{border:1px solid #ddd;padding:10px 14px;text-align:left}
th{background:#f0f0f0;font-weight:600}
tr:nth-child(even){background:#fafafa}
</style>
<p><script>
(function(){
  if(window.__lcCodeEnhanced)return;
  window.__lcCodeEnhanced=true;
  function enhance(){
    document.querySelectorAll('pre').forEach(function(pre){
      var code=pre.querySelector('code');
      var lang='';
      if(code){var m=(code.className||'').match(/language-(\S+)/);if(m)lang=m[1].toLowerCase();}
      if(lang)pre.setAttribute('data-lang',lang);
      if(lang){var badge=document.createElement('span');badge.className='lc-lang-badge';badge.textContent=lang;pre.insertBefore(badge,pre.firstChild);}
      var btn=document.createElement('button');
      btn.className='lc-copy-btn';btn.textContent='Copy';btn.setAttribute('aria-label','Copy code to clipboard');
      pre.appendChild(btn);
      btn.addEventListener('click',function(){
        var text=code?code.innerText:pre.innerText;
        if(navigator.clipboard&&window.isSecureContext){
          navigator.clipboard.writeText(text).then(function(){ok(btn);}).catch(function(){fb(text,btn);});
        }else{fb(text,btn);}
      });
    });
  }
  function ok(btn){btn.textContent='Copied!';btn.classList.add('copied');setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},2000);}
  function fb(text,btn){
    try{var ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;left:-9999px;top:-9999px;opacity:0';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);ok(btn);}
    catch(e){btn.textContent='✗ Failed';setTimeout(function(){btn.textContent='Copy';},2000);}
  }
  if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',enhance);}else{enhance();}
})();
</script><br />
<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 6</span> <span class="rt-label rt-postfix">minutes</span></span></p>
<p><!-- ============================================================
     LINUXCENT.COM — WordPress HTML Post
     Title : What Is eBPF? A Plain-English Guide for Linux and Kubernetes Engineers
     Series: eBPF: From Kernel to Cloud — Episode 1
     Author: Vamshi Krishna Santhapuri
     Paste into: Gutenberg → Custom HTML block
                 OR Classic editor → Text tab
     Font  : WordPress system font stack — no external imports needed
     ============================================================ --></p>
<p><!-- ============================================================
     LINUXCENT.COM — Episode 1
     Paste into: WordPress Classic editor → Text tab
     OR Gutenberg → Custom HTML block
     Style: Matches linuxcent.com native theme — no custom CSS needed
     ============================================================ --></p>
<p><em>~1,900 words &middot; Reading time: 7 min &middot; Series: eBPF: From Kernel to Cloud, Episode 1 of 18</em></p>
<p>Your Linux kernel has had a technology built into it since 2014 that most engineers working with Linux every day have never looked at directly. You have almost certainly been using it &mdash; through Cilium, Falco, Datadog, or even systemd &mdash; without knowing it was there.</p>
<p>This post is the plain-English introduction to eBPF that I wished existed when I first encountered it. No kernel engineering background required. No bytecode, no BPF maps, no JIT compilation. Just a clear answer to the question every Linux admin and DevOps engineer eventually asks: <strong>what actually is eBPF, and why does it matter for the infrastructure I run every day?</strong></p>
<hr />
<div id="ez-toc-container" class="ez-toc-v2_0_82_2 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction">
<div class="ez-toc-title-container">
<p class="ez-toc-title" style="cursor:inherit">Table of Contents</p>
<p><span class="ez-toc-title-toggle"><a href="#" class="ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle" aria-label="Toggle Table of Content"><span class="ez-toc-js-icon-con"><span class=""><span class="eztoc-hide" style="display:none;">Toggle</span><span class="ez-toc-icon-toggle-span"><svg style="fill: #999;color:#999" xmlns="http://www.w3.org/2000/svg" class="list-377408" width="20px" height="20px" viewBox="0 0 24 24" fill="none"><path d="M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z" fill="currentColor"></path></svg><svg style="fill: #999;color:#999" class="arrow-unsorted-368013" xmlns="http://www.w3.org/2000/svg" width="10px" height="10px" viewBox="0 0 24 24" version="1.2" baseProfile="tiny"><path d="M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z"/></svg></span></span></span></a></span></div>
<nav>
<ul class='ez-toc-list ez-toc-list-level-1 ' >
<li class='ez-toc-page-1 ez-toc-heading-level-2'><a class="ez-toc-link ez-toc-heading-1" href="https://linuxcent.com/what-is-ebpf-linux-kubernetes/#First_Forget_the_Name" >First: Forget the Name</a></li>
<li class='ez-toc-page-1 ez-toc-heading-level-2'><a class="ez-toc-link ez-toc-heading-2" href="https://linuxcent.com/what-is-ebpf-linux-kubernetes/#What_the_Linux_Kernel_Can_See_That_Nothing_Else_Can" >What the Linux Kernel Can See That Nothing Else Can</a></li>
<li class='ez-toc-page-1 ez-toc-heading-level-2'><a class="ez-toc-link ez-toc-heading-3" href="https://linuxcent.com/what-is-ebpf-linux-kubernetes/#The_Problem_eBPF_Solves_%E2%80%94_A_Real_Kubernetes_Scenario" >The Problem eBPF Solves &mdash; A Real Kubernetes Scenario</a>
<ul class='ez-toc-list-level-3' >
<li class='ez-toc-heading-level-3'><a class="ez-toc-link ez-toc-heading-4" href="https://linuxcent.com/what-is-ebpf-linux-kubernetes/#The_old_approaches_and_their_problems" >The old approaches and their problems</a></li>
<li class='ez-toc-page-1 ez-toc-heading-level-3'><a class="ez-toc-link ez-toc-heading-5" href="https://linuxcent.com/what-is-ebpf-linux-kubernetes/#The_eBPF_approach" >The eBPF approach</a></li>
</ul>
</li>
<li class='ez-toc-page-1 ez-toc-heading-level-2'><a class="ez-toc-link ez-toc-heading-6" href="https://linuxcent.com/what-is-ebpf-linux-kubernetes/#Tools_You_Are_Probably_Already_Running_on_eBPF" >Tools You Are Probably Already Running on eBPF</a></li>
<li class='ez-toc-page-1 ez-toc-heading-level-2'><a class="ez-toc-link ez-toc-heading-7" href="https://linuxcent.com/what-is-ebpf-linux-kubernetes/#eBPF_vs_the_Old_Ways" >eBPF vs the Old Ways</a></li>
<li class='ez-toc-page-1 ez-toc-heading-level-2'><a class="ez-toc-link ez-toc-heading-8" href="https://linuxcent.com/what-is-ebpf-linux-kubernetes/#Is_It_Safe_to_Run_in_Production" >Is It Safe to Run in Production?</a></li>
<li class='ez-toc-page-1 ez-toc-heading-level-2'><a class="ez-toc-link ez-toc-heading-9" href="https://linuxcent.com/what-is-ebpf-linux-kubernetes/#Common_Misconceptions" >Common Misconceptions</a></li>
<li class='ez-toc-page-1 ez-toc-heading-level-2'><a class="ez-toc-link ez-toc-heading-10" href="https://linuxcent.com/what-is-ebpf-linux-kubernetes/#Kernel_Version_Requirements" >Kernel Version Requirements</a></li>
<li class='ez-toc-page-1 ez-toc-heading-level-2'><a class="ez-toc-link ez-toc-heading-11" href="https://linuxcent.com/what-is-ebpf-linux-kubernetes/#The_Bottom_Line" >The Bottom Line</a></li>
<li class='ez-toc-page-1 ez-toc-heading-level-2'><a class="ez-toc-link ez-toc-heading-12" href="https://linuxcent.com/what-is-ebpf-linux-kubernetes/#Further_Reading" >Further Reading</a></li>
</ul>
</nav>
</div>
<p style="font-size:0.72em;font-weight:700;letter-spacing:0.12em;color:#f59e0b;text-transform:uppercase;margin:2em 0 0.75em 0;text-align:center;">Architecture Overview</p>
<figure class="wp-block-image size-full" style="margin:0 0 0.5em 0;">
<img loading="lazy" decoding="async" width="2400" height="2304" src="https://linuxcent.com/wp-content/uploads/2026/05/ep01-what-is-ebpf-og-2.png" alt="What Is eBPF — architecture diagram showing eBPF program types, verifier, JIT compiler, and kernel hook points" class="wp-image-2108" style="width:100%;height:auto;display:block;border-radius:8px;" srcset="https://linuxcent.com/wp-content/uploads/2026/05/ep01-what-is-ebpf-og-2.png 2400w, https://linuxcent.com/wp-content/uploads/2026/05/ep01-what-is-ebpf-og-2-300x288.png 300w, https://linuxcent.com/wp-content/uploads/2026/05/ep01-what-is-ebpf-og-2-1024x983.png 1024w, https://linuxcent.com/wp-content/uploads/2026/05/ep01-what-is-ebpf-og-2-768x737.png 768w, https://linuxcent.com/wp-content/uploads/2026/05/ep01-what-is-ebpf-og-2-1536x1475.png 1536w, https://linuxcent.com/wp-content/uploads/2026/05/ep01-what-is-ebpf-og-2-2048x1966.png 2048w" sizes="auto, (max-width: 2400px) 100vw, 2400px" /><figcaption style="text-align:center;font-size:0.85em;color:#6b7280;margin-top:0.75em;">eBPF sits between user space and the kernel — attaching programs to hook points without modifying kernel source.</figcaption></figure>
<hr style="border:none;border-top:1px solid #e5e7eb;margin:0.5em 0 2em 0;"/>
<h2 id="tldr">TL;DR</h2>
<ul>
<li>eBPF lets you run small, safe programs inside the Linux kernel — no kernel module, no reboot, no application changes required</li>
<li>The name is a historical artefact; modern eBPF is a general-purpose kernel observability and networking platform, not a packet filter</li>
<li>Programs attach to kernel hook points (tracepoints, kprobes, socket filters) — giving you visibility into every syscall, file open, and network packet</li>
<li>You are probably already running eBPF: Cilium, Falco, Datadog, and systemd all use it under the hood</li>
<li>Safe for production because the BPF verifier rejects any program that could crash or loop — covered in depth in <a href="/bpf-verifier-ebpf-safety/">EP02</a></li>
<li>Full feature set from Linux 5.8+; meaningful production use from Linux 4.14+ (most EKS and GKE defaults qualify)</li>
</ul>
<hr />
<h2><span class="ez-toc-section" id="First_Forget_the_Name"></span>First: Forget the Name<span class="ez-toc-section-end"></span></h2>
<p>eBPF stands for <em>extended Berkeley Packet Filter</em>. It is one of the most misleading names in computing for what the technology actually does.</p>
<p>The original BPF was a 1992 mechanism for filtering network packets &mdash; the engine behind <code class="" data-line="">tcpdump</code>. The extended version, introduced in Linux 3.18 (2014) and significantly matured through Linux 5.x, is a completely different technology. It is no longer just about packets. It is no longer just about filtering.</p>
<p>Forget the name. Here is what eBPF actually is:</p>
<blockquote>
<p>eBPF lets you run small, safe programs directly inside the Linux kernel &mdash; without writing a kernel module, without rebooting, and without modifying your applications.</p>
</blockquote>
<p>That is the complete definition. Everything else is implementation detail. The one-liner above is what matters for how you use it day to day.</p>
<hr />
<h2><span class="ez-toc-section" id="What_the_Linux_Kernel_Can_See_That_Nothing_Else_Can"></span>What the Linux Kernel Can See That Nothing Else Can<span class="ez-toc-section-end"></span></h2>
<p>To understand why eBPF is significant, you need to understand what the Linux kernel already sees on every server and every Kubernetes node you run.</p>
<p>The kernel is the lowest layer of software on your machine. Every action that happens &mdash; every file opened, every process started, every network packet sent &mdash; passes through the kernel. That means it has a complete, real-time view of everything:</p>
<ul>
<li><strong>Every syscall</strong> &mdash; every <code class="" data-line="">open()</code>, <code class="" data-line="">execve()</code>, <code class="" data-line="">connect()</code>, <code class="" data-line="">write()</code> from every process in every container on the node, in real time</li>
<li><strong>Every network packet</strong> &mdash; source, destination, port, protocol, bytes, and latency for every pod-to-pod and pod-to-external connection</li>
<li><strong>Every process event</strong> &mdash; every fork, exec, and exit, including processes spawned inside containers that your container runtime never reports</li>
<li><strong>Every file access</strong> &mdash; which process opened which file, when, and with what permissions, across all workloads on the node simultaneously</li>
<li><strong>CPU and memory usage</strong> &mdash; per-process CPU time, function-level latency, and memory allocation patterns without profiling agents</li>
</ul>
<p>The kernel has always had this visibility. The problem was that there was no safe, practical way to access it without writing kernel modules &mdash; which are complex, kernel version-specific, and genuinely dangerous to run in production. eBPF is the safe, practical way to access it.</p>
<hr />
<h2><span class="ez-toc-section" id="The_Problem_eBPF_Solves_%E2%80%94_A_Real_Kubernetes_Scenario"></span>The Problem eBPF Solves &mdash; A Real Kubernetes Scenario<span class="ez-toc-section-end"></span></h2>
<p>Here is a situation every Kubernetes engineer has faced. A production pod starts behaving strangely &mdash; elevated CPU, slow responses, occasional connection failures. You want to understand what is happening at a low level: what syscalls is it making, what network connections is it opening, is something spawning unexpected processes?</p>
<h3><span class="ez-toc-section" id="The_old_approaches_and_their_problems"></span>The old approaches and their problems<span class="ez-toc-section-end"></span></h3>
<p><strong>Restart the pod with a debug sidecar.</strong> You lose the current state immediately. The issue may not reproduce. You have modified the workload.</p>
<p><strong>Run strace inside the container via <code class="" data-line="">kubectl exec</code>.</strong> strace uses ptrace, which adds 50&ndash;100% CPU overhead to the traced process and is unavailable in hardened containers. You are tracing one process at a time with no cluster-wide view.</p>
<p><strong>Poll <code class="" data-line="">/proc</code> with a monitoring agent.</strong> Snapshot-based. Any event that happens between polls is invisible. A process that starts, does something, and exits between intervals is completely missed.</p>
<h3><span class="ez-toc-section" id="The_eBPF_approach"></span>The eBPF approach<span class="ez-toc-section-end"></span></h3>
<pre><code class="" data-line=""># Use a debug pod on the node — no changes to your workload
$ kubectl debug node/your-node -it --image=cilium/hubble-cli

# Real-time kernel events from every container on this node:
sys_enter_execve  pid=8821  comm=sh    args=[&quot;/bin/sh&quot;,&quot;-c&quot;,&quot;curl http://...&quot;]
sys_enter_connect pid=8821  comm=curl  dst=203.0.113.42:443
sys_enter_openat  pid=8821  comm=curl  path=/etc/passwd

# Something inside the pod spawned a shell, made an outbound connection,
# and read /etc/passwd — all visible without touching the pod.</code></pre>
<p>Real-time visibility. No overhead on your workload. Nothing restarted. Nothing modified. That is what eBPF makes possible.</p>
<hr />
<h2><span class="ez-toc-section" id="Tools_You_Are_Probably_Already_Running_on_eBPF"></span>Tools You Are Probably Already Running on eBPF<span class="ez-toc-section-end"></span></h2>
<p>eBPF is not a standalone product &mdash; it is the foundation that many tools in the cloud-native ecosystem are built on. You may already be running eBPF on your nodes without thinking about it explicitly.</p>
<table>
<thead>
<tr>
<th>Tool</th>
<th>What eBPF does for it</th>
<th>Without eBPF</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Cilium</strong></td>
<td>Replaces kube-proxy and iptables with kernel-level packet routing. 2&ndash;3&times; faster at scale.</td>
<td>iptables rules &mdash; linear lookup, degrades with service count</td>
</tr>
<tr>
<td><strong>Falco</strong></td>
<td>Watches every syscall in every container for security rule violations. Sub-millisecond detection.</td>
<td>Kernel module (risky) or ptrace (high overhead)</td>
</tr>
<tr>
<td><strong>Tetragon</strong></td>
<td>Runtime security enforcement &mdash; can kill a process or drop a network packet at the kernel level.</td>
<td>No practical alternative at this detection speed</td>
</tr>
<tr>
<td><strong>Datadog Agent</strong></td>
<td>Network performance monitoring and universal service monitoring without application code changes.</td>
<td>Language-specific agents injected into application code</td>
</tr>
<tr>
<td><strong>systemd</strong></td>
<td>cgroup resource accounting and network traffic control on your Linux nodes.</td>
<td>Legacy cgroup v1 interfaces with limited visibility</td>
</tr>
</tbody>
</table>
<hr />
<h2><span class="ez-toc-section" id="eBPF_vs_the_Old_Ways"></span>eBPF vs the Old Ways<span class="ez-toc-section-end"></span></h2>
<p>Before eBPF, getting deep visibility into a running Linux system meant choosing between three approaches, each with a significant trade-off:</p>
<table>
<thead>
<tr>
<th>Approach</th>
<th>Visibility</th>
<th>Cost</th>
<th>Production safe?</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Kernel modules</strong></td>
<td>Full kernel access</td>
<td>One bug = kernel panic. Version-specific, must recompile per kernel update.</td>
<td>No</td>
</tr>
<tr>
<td><strong>ptrace / strace</strong></td>
<td>One process at a time</td>
<td>50&ndash;100% CPU overhead on the traced process. Unusable in production.</td>
<td>No</td>
</tr>
<tr>
<td><strong>Polling /proc</strong></td>
<td>Snapshots only</td>
<td>Events between polls are invisible. Short-lived processes are missed entirely.</td>
<td>Partial</td>
</tr>
<tr>
<td><strong>eBPF</strong></td>
<td>Full kernel visibility</td>
<td>1&ndash;3% overhead. Verifier-guaranteed safety. Real-time stream, not polling.</td>
<td>Yes</td>
</tr>
</tbody>
</table>
<hr />
<h2><span class="ez-toc-section" id="Is_It_Safe_to_Run_in_Production"></span>Is It Safe to Run in Production?<span class="ez-toc-section-end"></span></h2>
<p>This is always the first question from any experienced Linux admin, and it is exactly the right question to ask. The answer is yes &mdash; and the reason is the <strong>BPF verifier</strong>.</p>
<p>Before any eBPF program is allowed to run on your node, the Linux kernel runs it through a built-in static safety analyser. This analyser examines every possible execution path and asks: could this program crash the kernel, loop forever, or access memory it should not?</p>
<p>If the answer is yes &mdash; or even <em>maybe</em> &mdash; the program is rejected at load time. It never runs.</p>
<blockquote>
<p><strong>This is fundamentally different from kernel modules.</strong> A kernel module loads immediately with no safety check. If it has a bug, you find out at runtime &mdash; usually as a kernel panic. An eBPF program that would cause a panic is rejected before it ever loads. The safety guarantee is mathematical, not hopeful.</p>
</blockquote>
<p>Episode 2 of this series covers the BPF verifier in full: what it checks, how it makes Cilium and Falco safe on your production nodes, and what questions to ask eBPF tool vendors about their implementation.</p>
<hr />
<h2><span class="ez-toc-section" id="Common_Misconceptions"></span>Common Misconceptions<span class="ez-toc-section-end"></span></h2>
<p><strong>eBPF is not a specific tool or product.</strong> It is a kernel technology &mdash; a platform. Cilium, Falco, Tetragon, and Pixie are tools built on top of it. When a vendor says &ldquo;we use eBPF&rdquo;, they mean they build on this kernel capability, not that they share a single implementation.</p>
<p><strong>eBPF is not only for networking.</strong> The Berkeley Packet Filter name suggests networking, but modern eBPF covers security, observability, performance profiling, and tracing. The networking origin is historical, not a limitation.</p>
<p><strong>eBPF is not only for Kubernetes.</strong> It works on any Linux system running kernel 4.9+, including bare metal servers, Docker hosts, and VMs. K8s is the most popular deployment target because of the observability challenges at scale, but it is not a requirement.</p>
<p><strong>You do not need to write eBPF programs to benefit from eBPF.</strong> Most Linux admins and DevOps engineers will use eBPF through tools like Cilium, Falco, and Datadog &mdash; never writing a line of BPF code themselves. This series covers the writing side later. Understanding what eBPF is makes you a significantly better user of these tools today.</p>
<hr />
<h2><span class="ez-toc-section" id="Kernel_Version_Requirements"></span>Kernel Version Requirements<span class="ez-toc-section-end"></span></h2>
<p>eBPF is a Linux kernel feature. The capabilities available depend directly on the kernel version running on your nodes. Run <code class="" data-line="">uname -r</code> on any node to check.</p>
<table>
<thead>
<tr>
<th>Kernel</th>
<th>What becomes available</th>
</tr>
</thead>
<tbody>
<tr>
<td><code class="" data-line="">4.9+</code></td>
<td>Basic eBPF support. Tracing, socket filtering. Most production systems today meet this minimum.</td>
</tr>
<tr>
<td><code class="" data-line="">5.4+</code></td>
<td>BTF (BPF Type Format) and CO-RE &mdash; programs that adapt to different kernel versions without recompile. Recommended minimum for production tooling.</td>
</tr>
<tr>
<td><code class="" data-line="">5.8+</code></td>
<td>Ring buffers for high-performance event streaming. Global variables. The target kernel for Cilium, Falco, and Tetragon full feature support.</td>
</tr>
<tr>
<td><code class="" data-line="">6.x</code></td>
<td>Open-coded iterators, improved verifier, LSM security enforcement hooks. Amazon Linux 2023 and Ubuntu 22.04+ ship 5.15 or newer and are fully eBPF-ready.</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>EKS users:</strong> Amazon Linux 2023 AMIs ship with kernel 6.1+ and support the full modern eBPF feature set out of the box. If you are still on AL2, the migration also resolves the NetworkManager deprecation issues covered in the <a href="https://linuxcent.com/eks-1-33-networkmanager-systemd-networkd-migration-fix/">EKS 1.33 post</a>.</p>
</blockquote>
<hr />
<h2><span class="ez-toc-section" id="The_Bottom_Line"></span>The Bottom Line<span class="ez-toc-section-end"></span></h2>
<p>eBPF is the answer to a question Linux engineers have been asking for years: how do I get deep visibility into what is happening on my servers and Kubernetes nodes &mdash; without adding massive overhead, injecting sidecars, or risking a kernel panic?</p>
<p>The answer is: run small, safe programs at the kernel level, where everything is already visible. Let the BPF verifier guarantee those programs are safe before they run. Stream the results to your observability tools through shared memory maps.</p>
<p>The tools you already use &mdash; Cilium for networking, Falco for security, Datadog for APM &mdash; are built on this foundation. Understanding eBPF means understanding <em>why</em> those tools work the way they do, <em>what</em> they can and cannot see, and <em>how</em> to evaluate new tools that claim to use it.</p>
<blockquote>
<p>Every eBPF-based tool you run on your nodes passed through the BPF verifier before it touched your cluster. Episode 2 covers exactly what that means &mdash; and why it matters for your infrastructure decisions.</p>
</blockquote>
<hr />
<h2><span class="ez-toc-section" id="Further_Reading"></span>Further Reading<span class="ez-toc-section-end"></span></h2>
<ul>
<li><a href="https://ebpf.io/what-is-ebpf/" target="_blank" rel="noopener noreferrer">ebpf.io &mdash; What is eBPF? (official introduction)</a></li>
<li><a href="https://docs.cilium.io/en/stable/concepts/ebpf/" target="_blank" rel="noopener noreferrer">Cilium documentation: eBPF dataplane explained</a></li>
<li><a href="https://falco.org/docs/event-sources/kernel/" target="_blank" rel="noopener noreferrer">Falco: kernel event sources and eBPF driver</a></li>
<li><a href="https://isovalent.com/blog/post/ebpf-documentary/" target="_blank" rel="noopener noreferrer">Isovalent: the story behind eBPF</a></li>
<li><a href="https://www.brendangregg.com/ebpf.html" target="_blank" rel="noopener noreferrer">Brendan Gregg&rsquo;s eBPF reference page</a></li>
</ul>
<hr />
<p><em>Questions or corrections? Reach me on <a href="https://www.linkedin.com/in/vamshikrishnasanthapuri/" target="_blank" rel="noopener noreferrer">LinkedIn</a>. If this was useful, the full series index is on <a href="https://linuxcent.com">linuxcent.com</a> &mdash; search the <strong>eBPF Series</strong> tag for all episodes.</em></p>
<p><a class="a2a_button_mastodon" href="https://www.addtoany.com/add_to/mastodon?linkurl=https%3A%2F%2Flinuxcent.com%2Fwhat-is-ebpf-linux-kubernetes%2F&amp;linkname=What%20Is%20eBPF%3F%20A%20Plain-English%20Guide%20for%20Linux%20and%20Kubernetes%20Engineers" title="Mastodon" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_email" href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Flinuxcent.com%2Fwhat-is-ebpf-linux-kubernetes%2F&amp;linkname=What%20Is%20eBPF%3F%20A%20Plain-English%20Guide%20for%20Linux%20and%20Kubernetes%20Engineers" title="Email" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_whatsapp" href="https://www.addtoany.com/add_to/whatsapp?linkurl=https%3A%2F%2Flinuxcent.com%2Fwhat-is-ebpf-linux-kubernetes%2F&amp;linkname=What%20Is%20eBPF%3F%20A%20Plain-English%20Guide%20for%20Linux%20and%20Kubernetes%20Engineers" title="WhatsApp" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_reddit" href="https://www.addtoany.com/add_to/reddit?linkurl=https%3A%2F%2Flinuxcent.com%2Fwhat-is-ebpf-linux-kubernetes%2F&amp;linkname=What%20Is%20eBPF%3F%20A%20Plain-English%20Guide%20for%20Linux%20and%20Kubernetes%20Engineers" title="Reddit" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_x" href="https://www.addtoany.com/add_to/x?linkurl=https%3A%2F%2Flinuxcent.com%2Fwhat-is-ebpf-linux-kubernetes%2F&amp;linkname=What%20Is%20eBPF%3F%20A%20Plain-English%20Guide%20for%20Linux%20and%20Kubernetes%20Engineers" title="X" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Flinuxcent.com%2Fwhat-is-ebpf-linux-kubernetes%2F&amp;linkname=What%20Is%20eBPF%3F%20A%20Plain-English%20Guide%20for%20Linux%20and%20Kubernetes%20Engineers" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_copy_link" href="https://www.addtoany.com/add_to/copy_link?linkurl=https%3A%2F%2Flinuxcent.com%2Fwhat-is-ebpf-linux-kubernetes%2F&amp;linkname=What%20Is%20eBPF%3F%20A%20Plain-English%20Guide%20for%20Linux%20and%20Kubernetes%20Engineers" title="Copy Link" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Flinuxcent.com%2Fwhat-is-ebpf-linux-kubernetes%2F&#038;title=What%20Is%20eBPF%3F%20A%20Plain-English%20Guide%20for%20Linux%20and%20Kubernetes%20Engineers" data-a2a-url="https://linuxcent.com/what-is-ebpf-linux-kubernetes/" data-a2a-title="What Is eBPF? A Plain-English Guide for Linux and Kubernetes Engineers"></a></p><p>The post <a href="https://linuxcent.com/what-is-ebpf-linux-kubernetes/">What Is eBPF? A Plain-English Guide for Linux and Kubernetes Engineers</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxcent.com/what-is-ebpf-linux-kubernetes/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1429</post-id>	</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 

Served from: linuxcent.com @ 2026-08-31 07:30:55 by W3 Total Cache
-->