<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by digant patel on Medium]]></title>
        <description><![CDATA[Stories by digant patel on Medium]]></description>
        <link>https://medium.com/@digantp90?source=rss-fe44fe23deb------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*KHmGxDpMZxSYup3r</url>
            <title>Stories by digant patel on Medium</title>
            <link>https://medium.com/@digantp90?source=rss-fe44fe23deb------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 17 May 2026 17:15:55 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@digantp90/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[eBPF: Hello World Or: how I learned to run code in the kernel without crying ]]></title>
            <link>https://medium.com/@digantp90/ebpf-hello-world-or-how-i-learned-to-run-code-in-the-kernel-without-crying-6e2dda7b0228?source=rss-fe44fe23deb------2</link>
            <guid isPermaLink="false">https://medium.com/p/6e2dda7b0228</guid>
            <category><![CDATA[observability]]></category>
            <category><![CDATA[linux-kernel]]></category>
            <category><![CDATA[ebpf]]></category>
            <category><![CDATA[linux]]></category>
            <category><![CDATA[security]]></category>
            <dc:creator><![CDATA[digant patel]]></dc:creator>
            <pubDate>Sun, 17 May 2026 17:10:59 GMT</pubDate>
            <atom:updated>2026-05-17T17:10:59.957Z</atom:updated>
            <content:encoded><![CDATA[<p>Last article ended with <em>“and now you don’t have to write a kernel module.”</em> This is what you write instead.</p><p>We’ll do your first eBPF program three ways — a one-liner, a Python script, and a C++ loader — in about fifteen minutes. No kernel modules. No reboots.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qR872Qm6bI_JvUJ5VP3WOA.png" /></figure><h3>Python First, C++ Second</h3><p>I’ll write this three times, starting with Python and ending in C++. Python first because BCC handles the compile, load, and verify steps for you — so the first time you see eBPF run, nothing’s hidden behind a build system. Once it clicks, we’ll redo it in C++ to see what BCC was doing on your behalf.</p><h3>Let’s Write It</h3><pre>#!/usr/bin/env python3<br>from bcc import BPF<br><br>program = r&quot;&quot;&quot;<br>int hello(void *ctx) {<br>    char comm[16];<br>    bpf_get_current_comm(&amp;comm, sizeof(comm));<br>    bpf_trace_printk(&quot;hello from %s\n&quot;, comm);<br>    return 0;<br>}<br>&quot;&quot;&quot;<br><br>b = BPF(text=program)<br>syscall = b.get_syscall_fnname(&quot;execve&quot;)<br>b.attach_kprobe(event=syscall, fn_name=&quot;hello&quot;)<br><br>print(&quot;tracing every execve on the system. ctrl-c to exit.&quot;)<br>b.trace_print()</pre><h3><em>Under the Hood</em></h3><pre>   ┌─────────────────────────────────────────────────────┐<br>   │                       Hello.py                      │<br>   └─────────────────────────────────────────────────────┘<br><br>   BPF(text=program)<br>        │<br>        ├─►  clang compiles C string → BPF bytecode<br>        │<br>        ├─►  bpf() syscall: load into kernel<br>        │<br>        ├─►  verifier checks safety  ──► ✗ reject (raises exception)<br>        │                            └─► ✓ accept<br>        │<br>        └─►  JIT compiles to native machine code<br><br>   get_syscall_fnname(&quot;execve&quot;)<br>        │<br>        └─►  returns &quot;__x64_sys_execve&quot;  (or arm64 equivalent)<br><br>   attach_kprobe(event=syscall, fn_name=&quot;hello&quot;)<br>        │<br>        └─►  patches kernel function entry<br>             every execve now runs hello() first<br><br>   trace_print()<br>        │<br>        └─►  reads /sys/kernel/debug/tracing/trace_pipe<br>             prints to your terminal, blocks until ctrl-c<br><br><br>   ┌─────────────────────────────────────────────────────┐<br>   │                     THE KERNEL                      │<br>   │                                                     │<br>   │   any process calls execve                          │<br>   │        │                                            │<br>   │        ▼                                            │<br>   │   hello() runs  ──► bpf_trace_printk(...)           │<br>   │        │                       │                    │<br>   │        ▼                       ▼                    │<br>   │   real execve continues   trace_pipe                │<br>   │                                │                    │<br>   └────────────────────────────────┼────────────────────┘<br>                                    │<br>                                    ▼<br>                            your terminal</pre><p>That’s the easy version. One sensor, every program launch on the box.</p><p>BCC did four things for you behind that one BPF(...) call: <strong><em>compile, load, attach, tail</em></strong>. Great for learning. Not how eBPF ships to production.</p><p>Let’s do it again without the magic.</p><h3><em>Now Without the Magic</em></h3><p>Kernel Code</p><pre>// Kernel side code<br>// hello.bpf.c<br>#include &quot;vmlinux.h&quot;<br>#include &lt;bpf/bpf_helpers.h&gt;<br><br>char LICENSE[] SEC(&quot;license&quot;) = &quot;Dual BSD/GPL&quot;;<br><br>SEC(&quot;tracepoint/syscalls/sys_enter_execve&quot;)<br>int hello(void *ctx)<br>{<br>    char comm[16];<br>    bpf_get_current_comm(&amp;comm, sizeof(comm));<br>    bpf_printk(&quot;hello from %s\n&quot;, comm);<br>    return 0;<br>}</pre><p>Userspace code</p><pre>// Userspace code<br>// hello.cpp<br>#include &lt;iostream&gt;<br>#include &lt;unistd.h&gt;<br>#include &quot;hello.skel.h&quot;<br><br>int main() {<br>    auto* skel = hello_bpf__open_and_load();<br>    if (!skel) { std::cerr &lt;&lt; &quot;load failed\n&quot;; return 1; }<br><br>    if (hello_bpf__attach(skel)) {<br>        std::cerr &lt;&lt; &quot;attach failed\n&quot;;<br>        hello_bpf__destroy(skel);<br>        return 1;<br>    }<br><br>    std::cout &lt;&lt; &quot;tracing every execve on the system. ctrl-c to exit.\n&quot;;<br>    std::cout &lt;&lt; &quot;watch output in another terminal:\n&quot;;<br>    std::cout &lt;&lt; &quot;  sudo cat /sys/kernel/debug/tracing/trace_pipe\n&quot;;<br><br>    pause();   // sleep until ctrl-c<br><br>    hello_bpf__destroy(skel);<br>    return 0;<br>}</pre><p>Under the hood</p><pre>   ┌─────────────────────────────────────────────────────┐<br>   │              ONE-TIME SETUP (per machine)           │<br>   └─────────────────────────────────────────────────────┘<br><br>   bpftool btf dump file /sys/kernel/btf/vmlinux format c<br>        │<br>        └─►  vmlinux.h  (all kernel types, ~5 MB header)<br><br><br>   ┌─────────────────────────────────────────────────────┐<br>   │                  BUILD TIME (you)                   │<br>   └─────────────────────────────────────────────────────┘<br><br>   clang -target bpf -c hello.bpf.c<br>        │   #include &quot;vmlinux.h&quot;  ◄── pulled in here<br>        │<br>        └─►  C source  →  BPF bytecode (hello.bpf.o)<br><br>   bpftool gen skeleton hello.bpf.o<br>        │<br>        └─►  generates hello.skel.h  (typed loader functions)<br><br>   clang++ hello.cpp -lbpf<br>        │<br>        └─►  your binary (hello)<br><br><br>   ┌─────────────────────────────────────────────────────┐<br>   │                   YOUR C++ BINARY                   │<br>   └─────────────────────────────────────────────────────┘<br><br>   hello_bpf__open_and_load()<br>        │<br>        ├─►  reads embedded bytecode from hello.skel.h<br>        │<br>        ├─►  bpf() syscall: load into kernel<br>        │<br>        ├─►  verifier checks safety  ──► ✗ reject (returns NULL)<br>        │                            └─► ✓ accept<br>        │<br>        └─►  JIT compiles to native machine code<br><br>   hello_bpf__attach()<br>        │<br>        └─►  reads SEC(&quot;tracepoint/...&quot;) annotation<br>             attaches program to that hook<br><br>   pause()<br>        │<br>        └─►  sleeps until ctrl-c<br>             (kernel-side keeps firing the whole time)<br><br>   hello_bpf__destroy()<br>        │<br>        └─►  detaches, unloads, frees<br><br><br>   ┌─────────────────────────────────────────────────────┐<br>   │                     THE KERNEL                      │<br>   │                                                     │<br>   │   any process calls execve                          │<br>   │        │                                            │<br>   │        ▼                                            │<br>   │   hello() runs  ──► bpf_printk(...)                 │<br>   │        │                       │                    │<br>   │        ▼                       ▼                    │<br>   │   real execve continues   trace_pipe                │<br>   │                                │                    │<br>   └────────────────────────────────┼────────────────────┘<br>                                    │<br>                                    ▼<br>                       sudo cat /sys/kernel/debug/<br>                            tracing/trace_pipe</pre><p>Same program. Same sensor. Same output.</p><p>What changed: clang ran at build time, not runtime. The binary stands alone. The four loader calls — <strong><em>open, load, attach, destroy </em></strong>— are yours to see, not buried inside BCC.</p><p>This is eBPF without the training wheels. Same physics, exposed plumbing.</p><h3><strong>Same Program, Two Paths</strong></h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Rf4_uOHeLfo9LVOGMESWAA.png" /><figcaption>Comparision C++ v/s python</figcaption></figure><h3>Up Next</h3><p>Two questions this article didn’t answer, on purpose:</p><ul><li>What does the JIT actually compile your eBPF into? You can see the assembly. It’s worth seeing.</li><li>How does one binary run across kernel versions when struct offsets change between releases? That’s CO-RE, and it’s the reason eBPF ships at scale.</li></ul><p>Both deserve their own articles. Both are coming.</p><p>Follow if you want them when they land …</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6e2dda7b0228" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[eBPF, Explained for People Who’ve Never Written a Kernel Module]]></title>
            <link>https://medium.com/@digantp90/ebpf-explained-for-people-whove-never-written-a-kernel-module-adbebc554b4f?source=rss-fe44fe23deb------2</link>
            <guid isPermaLink="false">https://medium.com/p/adbebc554b4f</guid>
            <category><![CDATA[observability]]></category>
            <category><![CDATA[ebpf]]></category>
            <category><![CDATA[linux-kernel]]></category>
            <category><![CDATA[system-programming]]></category>
            <category><![CDATA[linux]]></category>
            <dc:creator><![CDATA[digant patel]]></dc:creator>
            <pubDate>Thu, 14 May 2026 12:19:25 GMT</pubDate>
            <atom:updated>2026-05-15T05:40:40.201Z</atom:updated>
            <content:encoded><![CDATA[<p>“You keep hearing ‘eBPF’ in conference talks, blog posts, and job descriptions. This article is the explanation you needed before any of those made sense — written for people who’ve never touched kernel code, and don’t plan to.”</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*146KZOTH78y-w7zCZz4IRg.png" /></figure><p><strong>Userspace v/s Kernel space :</strong></p><pre>   ┌─────────────────────────┐<br>   │      USER SPACE         │<br>   │                         │<br>   │   apps   libraries      │<br>   └───────────┬─────────────┘<br>               │<br>            syscalls<br>               │<br>   ┌───────────▼─────────────┐<br>   │     KERNEL SPACE        │<br>   │                         │<br>   │   subsystems   drivers  │<br>   └───────────┬─────────────┘<br>               │<br>   ┌───────────▼─────────────┐<br>   │       HARDWARE          │<br>   └─────────────────────────┘</pre><p><strong>Single command from userspace</strong>:</p><pre>strace -c echo &quot;Hello, World!&quot;<br>Hello, World!<br>% time     seconds  usecs/call     calls    errors syscall<br>------ ----------- ----------- --------- --------- ----------------<br> 35.31    0.000131          14         9           mmap<br> 11.32    0.000042          14         3           openat<br> 10.78    0.000040          13         3           mprotect<br>  7.28    0.000027           5         5           close<br>  6.74    0.000025          25         1           munmap<br>  6.47    0.000024           6         4           fstat<br>  3.50    0.000013          13         1           write<br>  3.23    0.000012           4         3           brk<br>  2.70    0.000010           5         2           pread64<br>  2.70    0.000010          10         1         1 access<br>  1.89    0.000007           7         1           read<br>  1.62    0.000006           6         1           prlimit64<br>  1.62    0.000006           6         1           getrandom<br>  1.35    0.000005           5         1           arch_prctl<br>  1.35    0.000005           5         1           rseq<br>  1.08    0.000004           4         1           set_tid_address<br>  1.08    0.000004           4         1           set_robust_list<br>  0.00    0.000000           0         1           execve<br>------ ----------- ----------- --------- --------- ----------------<br>100.00    0.000371           9        40         1 total</pre><p>So a single echo triggers ~40 syscalls. If you want broad observability coverage, the syscall layer is the natural place to hook. The catch: doing that traditionally meant kernel modules, ptrace overhead, or auditd&#39;s firehose — none of them cheap. This is where eBPF comes in.</p><h3>Why eBPF Instead of the Alternatives?</h3><p>If you want code running in the kernel, you’ve historically had two options. Both are painful.</p><p><strong>Option 1: Change the kernel itself.</strong></p><pre>write patch  →  convince maintainers  →  merged upstream<br>                →  wait for kernel release<br>                →  wait for distros to ship it<br>                →  wait for users to upgrade<br>                                                    ⏱  ~1 year</pre><p>Right path for things that truly belong in the kernel. Absurd overhead for “count how many times a function runs.”</p><p><strong>Option 2: Write a kernel module.</strong></p><pre>edit  →  compile  →  insmod  →  💥 panic  →  reboot  →  repeat<br>   ⚠ no safety net      ⚠ ABI breaks across kernels<br>   ⚠ ships per-kernel   ⚠ once loaded, it IS the kernel</pre><p>Faster than upstreaming. Still painful — and one bug is a hung machine.</p><p><strong>Option 3: eBPF.</strong></p><pre>write  →  load  →  verifier ✓  →  attached  →  detach<br>                          │<br>                          └─ ✗ rejected if unsafe (never runs)<br>   ✓ no kernel rebuild    ✓ no reboot<br>   ✓ can&#39;t crash kernel   ✓ works across kernel versions</pre><p>Same outcome — code running in the kernel — without either of the costs above.</p><h4>The three options, side by side</h4><pre>                  KERNEL PATCH      KERNEL MODULE         eBPF<br>                  ────────────      ─────────────         ────<br>   speed to ship    ~1 year         days                  minutes<br>   reboot needed    yes             usually               no<br>   can crash box    yes             yes                   no<br>   ships how        kernel release  per-kernel binary     one binary<br>   trust model      full kernel     full kernel           sandboxed</pre><p><strong>The trade-off:</strong> you can’t do <em>anything</em> in eBPF. The verifier’s rules are strict, and some things genuinely need a kernel module or a kernel patch. But for “observe, filter, or decide when X happens in the kernel” — which is most of what people actually want — eBPF is the right tool.</p><h3>So What Is eBPF, Actually?</h3><p>The name is unhelpful. “Extended Berkeley Packet Filter” sounds like router config from 1998. The original BPF really was just a packet filter — what tcpdump uses under the hood. Today&#39;s eBPF has outgrown that name. Here&#39;s a simpler take:</p><blockquote><strong><em>eBPF lets you run small, safe programs inside the Linux kernel without writing a kernel module.</em></strong></blockquote><p>Three things make that work:</p><p><strong>1. The kernel checks your code before running it.</strong> You write a small program, compile it, and hand it to the kernel. A component called the <strong>verifier</strong> inspects it first — looking for things like infinite loops, bad memory access, anything that could crash the system. If it can’t prove your program is safe, it refuses to load it. No surprises at runtime.</p><p><strong>2. You attach it to an event.</strong> Your program does nothing until you hook it to something the kernel does: a system call, a network packet arriving, a function being called, a file being opened. When that event happens, your program runs. When it doesn’t, it costs nothing.</p><p><strong>3. It talks to your app through a shared mailbox.</strong> The kernel-side program writes events into a shared data structure called a <strong>map</strong>. Your normal app, running in userspace, reads from that map. That’s the whole communication model.</p><pre>   USER SPACE                  KERNEL SPACE<br>   ──────────                  ────────────<br>   ┌──────────┐               ┌──────────────┐<br>   │  your    │               │  eBPF prog   │<br>   │  app     │               │  on a hook   │<br>   └────┬─────┘               └──────┬───────┘<br>        │       ┌──────────┐         │<br>        └──────►│   MAP    │◄────────┘<br>         ◄──────│ (shared) │<br>                └──────────┘</pre><p>Put together, the loop looks like this:</p><pre>write a small program  →  kernel verifies it  →  attach to an event<br>                                              →  read results from a map</pre><p>No compile-against-kernel-headers. No reboot. No risk of taking the machine down. Compared to the kernel module loop from the last section, it’s a different sport.</p><h3>What Can You Actually Build With This?</h3><p>Three domains, one runtime.</p><h4>1. See what your system is doing (observability)</h4><p>Want to know which programs are opening files right now? One line:</p><pre>$ sudo bpftrace -e &#39;tracepoint:syscalls:sys_enter_openat<br>                       { @[comm] = count(); }&#39;<br>   @[bash]:        14<br>   @[chrome]:    1847<br>. . .</pre><p>No agent to install. No log pipeline. Just ask the kernel.</p><h4>2. Stop bad things from happening (security)</h4><p>When a program tries to run something, eBPF can step in and decide:</p><pre>program tries to run /tmp/sketchy<br>                │<br>                ▼<br>          eBPF checks<br>                │<br>        ┌───────┴───────┐<br>        ▼               ▼<br>      ALLOW           BLOCK</pre><p>This is how modern security tools work. Same idea: hook the event, decide inline.</p><h4>3. Make networking faster (networking)</h4><p>eBPF runs your code right where packets arrive, before the kernel even processes them:</p><pre>packet arrives<br>        │<br>        ▼<br>   ┌──────────────┐<br>   │  eBPF here   │──► drop  (bad traffic)<br>   │              │──► route (load balancing)<br>   │              │──► pass  (normal)<br>   └──────────────┘</pre><h4>The big picture</h4><pre>   OLD WORLD              NEW WORLD<br>  ─────────              ─────────<br>   observe          perf, logs, /proc      ┐<br>   secure           auditd, EDR modules    ├──►  eBPF<br>   network          iptables               ┘</pre><h3>Your first eBPF program will fail the verifier. That’s the point .</h3><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=adbebc554b4f" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>