---
title: eBPF Japan Meetup#6 Presentation
tags: 
author: [Naoya Tezuka](https://www.docswell.com/user/naoya-tezuka)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/DJY42LYQ7M.jpg?width=480
description: eBPF Japan Meetup#6 Presentation by Naoya Tezuka
published: March 31, 26
canonical: https://www.docswell.com/s/naoya-tezuka/K1QJPP-2026-03-31-210630
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/DJY42LYQ7M.jpg)

Building a WireGuard Data Plane
with eBPF and bpf_crypto
eBPF Meetup Tokyo #6, 2026
Naoya Tezuka
DISCLAIMER
This is a personal technical exploration and does not relate to any product, service, or internal project at my company. All content is
based on publicly available upstream Linux kernel source code and documentation. No confidential information is disclosed.


# Page. 2

![Page Image](https://bcdn.docswell.com/page/V7NY84P2E8.jpg)

Who
●
Naoya Tezuka
○
github: NT-marlowe
○
X InformationRes5
●
SWE
●
debug ChromeOS / Android kernel


# Page. 3

![Page Image](https://bcdn.docswell.com/page/YJ9PKQ3D73.jpg)

Agenda
●
Motivation &amp; bpf_crypto API Overview
●
Ideal Architecture
●
The Blocker: No AEAD
●
Compromises for PoC
●
PoC Demo
●
Takeaways &amp; Future


# Page. 4

![Page Image](https://bcdn.docswell.com/page/GJ8DRGM5JD.jpg)

What is WireGuard?
●
Modern VPN protocol, in-kernel since Linux 5.6
●
Uses ChaCha20Poly1305 AEAD for data encryption 2
●
○
ChaCha20: symmetric stream cipher
○
Poly1305 MAC to verify the integrity of data within VPN tunnel
Simple and easy to use, minimal codebase
[1] https://www.wireguard.com/trademark-policy/
[2] https://www.wireguard.com/protocol/


# Page. 5

![Page Image](https://bcdn.docswell.com/page/LJLMKG33ER.jpg)

What is WireGuard?
●
Modern VPN protocol, in-kernel since Linux 5.6
●
Uses ChaCha20Poly1305 AEAD for data encryption 2
●
○
ChaCha20: symmetric stream cipher
○
Poly1305 MAC to verify the integrity of data within VPN tunnel
Simple and easy to use, minimal codebase
…Looks like a good use case of bpf_crypto 🤔
[1] https://www.wireguard.com/trademark-policy/
[2] https://www.wireguard.com/protocol/


# Page. 6

![Page Image](https://bcdn.docswell.com/page/47MY2QDM7W.jpg)

What is bpf_crypto?
●
eBPF kfuncs that expose the kernel&#039;s crypto API to BPF programs
○
Encrypt/decrypt packets directly in TC/XDP hooks — no user-space
round-trip
●
Key design: sleepable/non-sleepable separation 1
○
Context creation (heavy, allocates memory) → sleepable SYSCALL program
2
○
Encrypt/decrypt (fast, per-packet) → non-sleepable TC/XDP program
○
Connected via BPF map (kptr)
[1] &quot;Sleepable BPF programs&quot;: https://lwn.net/Articles/825415/
[2] https://docs.ebpf.io/linux/program-type/BPF_PROG_TYPE_SYSCALL/


# Page. 7

![Page Image](https://bcdn.docswell.com/page/P7R9M84LE9.jpg)

bpf_crypto API Overview
●
Introduced in Linux 6.10 (by Vadim Fedorenko, Meta) 1
●
Create crypto ctx via SYSCALL programs, and acquire/release it
[1] https://lwn.net/Articles/1049463/


# Page. 8

![Page Image](https://bcdn.docswell.com/page/PJXQV8Z67X.jpg)

bpf_crypto API Overview
●
Introduced in Linux 6.10 (by Vadim Fedorenko, Meta) 1
●
Uses bpf_dynptr for input/output buffers
[1] https://lwn.net/Articles/1049463/


# Page. 9

![Page Image](https://bcdn.docswell.com/page/3JK9QK8GJD.jpg)

Why eBPF for WireGuard Data Plane?
●
Explore bpf_crypto&#039;s real-world capability
○
Usage examples are limited to kernel selftest 1
○
AF_XDP-based implementations exist 2 , but they move crypto to
user-space. Our goal: crypto stays in-kernel via bpf_crypto
●
Programmable data plane
○
wireguard.ko is monolithic
○
eBPF allows per-packet decisions: selective encryption, custom routing,
integration with observability
●
Learn bpf_crypto by building
[1] https://cocalc.com/github/torvalds/linux/blob/master/tools/testing/selftests/bpf/progs/crypto_bench.c
[2] https://cndp.io/guide/sample_app_ug/WireGuard.html


# Page. 10

![Page Image](https://bcdn.docswell.com/page/LE3WPZ25E5.jpg)

Ideal Architecture


# Page. 11

![Page Image](https://bcdn.docswell.com/page/8EDKLRZY7G.jpg)

Ideal Architecture
●
Layer 1 User-space Control Plane
Noise handshake Curve25519, derives symmetric session keys.
●
Layer 2: eBPF SYSCALL program (sleepable) Receives keys, calls
bpf_crypto_ctx_create(), stores crypto context in BPF Map.
●
Layer 3: eBPF XDP/TC Ingress
Parses incoming WireGuard UDP packets, decrypts payload via
bpf_crypto_decrypt().
●
Layer 4: eBPF TC Egress
Encrypts outgoing packets via bpf_crypto_encrypt(), adds WireGuard/UDP/IP
headers.


# Page. 12

![Page Image](https://bcdn.docswell.com/page/V7PK2WD2J8.jpg)

The Blocker: AEAD Not Supported
●
WireGuard requires: ChaCha20Poly1305 AEAD
●
bpf_crypto provides: skcipher only (unauthenticated stream/block ciphers)
○
●
We only have crypto/bpf_crypto_skcipher.c 1
No AEAD patch exists on LKML as of March 2026.
○
Confirmed with a committer of bpf_crypto.
[1] See https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/crypto .


# Page. 13

![Page Image](https://bcdn.docswell.com/page/2JVVD81XJQ.jpg)

What We Compromised for the PoC
Dropped
●
Poly1305 authentication AEAD
●
WireGuard protocol framing
●
Key rotation
●
WireGuard client compatibility
Kept
●
In-kernel encryption/decryption via
bpf_crypto
●
TC-based packet interception
●
Symmetric key provisioning via
SYSCALL program
●
Real packet processing


# Page. 14

![Page Image](https://bcdn.docswell.com/page/5EGLY52RJL.jpg)

What We Compromised for the PoC
No key rotation
No authentication
No authentication


# Page. 15

![Page Image](https://bcdn.docswell.com/page/4JQYMZPY7P.jpg)

PoC Setup


# Page. 16

![Page Image](https://bcdn.docswell.com/page/K74WV3YZE1.jpg)

PoC Env
●
Linux kernel 6.12+ with bpf_crypto support
○
●
Go 1.24
○
●
tested on 6.17.014-generic amd64
use ebpf-go for userspace implementation
Clang (for BPF compilation)


# Page. 17

![Page Image](https://bcdn.docswell.com/page/LJ1Y316DEG.jpg)

PoC Demo


# Page. 18

![Page Image](https://bcdn.docswell.com/page/GJWGP8W872.jpg)

Plaintext / Ciphertext Comparison
13:01:39.433769 IP 10.0.0.1 &gt; 10.0.0.2: ICMP echo request, id 25785, seq 1, length 64
0x0000: 4500 0054 463f 4000 4001 e067 0a00 0001 E..TF?@.@..g....
0x0010: 0a00 0002 0800 1147 64b9 0001 a323 ba69 .......Gd....#.i
0x0020: 0000 0000 5f9e 0600 0000 0000 1011 1213 ...._...........
0x0030: 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 .............!&quot;#
0x0040: 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 $%&amp;&#039;()*+,-./0123
0x0050: 3435 3637
plaintext
------------------------------------------------------------------------------------12:59:50.716750 IP 10.0.0.1 &gt; 10.0.0.2: ICMP type-#36, length 64
0x0000: 4500 0054 21fa 4000 4001 04ad 0a00 0001 E..T!.@.@.......
0x0010: 0a00 0002 24a3 e2a0 7ccd 1c1f 4b9c 652c ....$...|...K.e,
0x0020: c01d 33cc 2528 1a64 7b50 5a66 70fb 43e5 ..3.%(.d{PZfp.C.
0x0030: e764 fdcb 69e9 9913 1554 a427 b1dd 7bed .d..i....T.&#039;..{.
0x0040: 4581 093c 0c86 3b68 671d 0b01 31ea 158b E..&lt;..;hg...1...
0x0050: afe3 f913
ciphertext


# Page. 19

![Page Image](https://bcdn.docswell.com/page/4EZLK85973.jpg)

IP header stays cleartext — routing still works
13:01:39.433769 IP 10.0.0.1 &gt; 10.0.0.2: ICMP echo request, id 25785, seq 1, length 64
0x0000: 4500 0054 463f 4000 4001 e067 0a00 0001 E..TF?@.@..g....
0x0010: 0a00 0002 0800 1147 64b9 0001 a323 ba69 .......Gd....#.i
0x0020: 0000 0000 5f9e 0600 0000 0000 1011 1213 ...._...........
0x0030: 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 .............!&quot;#
0x0040: 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 $%&amp;&#039;()*+,-./0123
0x0050: 3435 3637
plaintext
-------------------------------------------------------------------------------------12:59:50.716750 IP 10.0.0.1 &gt; 10.0.0.2: ICMP type-#36, length 64
0x0000: 4500 0054 21fa 4000 4001 04ad 0a00 0001 E..T!.@.@.......
0x0010: 0a00 0002 24a3 e2a0 7ccd 1c1f 4b9c 652c ....$...|...K.e,
0x0020: c01d 33cc 2528 1a64 7b50 5a66 70fb 43e5 ..3.%(.d{PZfp.C.
0x0030: e764 fdcb 69e9 9913 1554 a427 b1dd 7bed .d..i....T.&#039;..{.
0x0040: 4581 093c 0c86 3b68 671d 0b01 31ea 158b E..&lt;..;hg...1...
0x0050: afe3 f913
ciphertext


# Page. 20

![Page Image](https://bcdn.docswell.com/page/Y76WQP9D7V.jpg)

tcpdump can&#039;t even identify the packet type
13:01:39.433769 IP 10.0.0.1 &gt; 10.0.0.2: ICMP echo request, id 25785, seq 1, length 64
0x0000: 4500 0054 463f 4000 4001 e067 0a00 0001 E..TF?@.@..g....
0x0010: 0a00 0002 0800 1147 64b9 0001 a323 ba69 .......Gd....#.i
0x0020: 0000 0000 5f9e 0600 0000 0000 1011 1213 ...._...........
0x0030: 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 .............!&quot;#
0x0040: 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 $%&amp;&#039;()*+,-./0123
0x0050: 3435 3637
plaintext
-------------------------------------------------------------------------------------12:59:50.716750 IP 10.0.0.1 &gt; 10.0.0.2: ICMP type-#36, length 64
0x0000: 4500 0054 21fa 4000 4001 04ad 0a00 0001 E..T!.@.@.......
0x0010: 0a00 0002 24a3 e2a0 7ccd 1c1f 4b9c 652c ....$...|...K.e,
0x0020: c01d 33cc 2528 1a64 7b50 5a66 70fb 43e5 ..3.%(.d{PZfp.C.
0x0030: e764 fdcb 69e9 9913 1554 a427 b1dd 7bed .d..i....T.&#039;..{.
0x0040: 4581 093c 0c86 3b68 671d 0b01 31ea 158b E..&lt;..;hg...1...
0x0050: afe3 f913
ciphertext


# Page. 21

![Page Image](https://bcdn.docswell.com/page/G75MRKN874.jpg)

Predictable padding → indistinguishable from random
13:01:39.433769 IP 10.0.0.1 &gt; 10.0.0.2: ICMP echo request, id 25785, seq 1, length 64
0x0000: 4500 0054 463f 4000 4001 e067 0a00 0001 E..TF?@.@..g....
0x0010: 0a00 0002 0800 1147 64b9 0001 a323 ba69 .......Gd....#.i
0x0020: 0000 0000 5f9e 0600 0000 0000 1011 1213 ...._...........
0x0030: 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 .............!&quot;#
0x0040: 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 $%&amp;&#039;()*+,-./0123
0x0050: 3435 3637
plaintext
-------------------------------------------------------------------------------------12:59:50.716750 IP 10.0.0.1 &gt; 10.0.0.2: ICMP type-#36, length 64
0x0000: 4500 0054 21fa 4000 4001 04ad 0a00 0001 E..T!.@.@.......
0x0010: 0a00 0002 24a3 e2a0 7ccd 1c1f 4b9c 652c ....$...|...K.e,
0x0020: c01d 33cc 2528 1a64 7b50 5a66 70fb 43e5 ..3.%(.d{PZfp.C.
0x0030: e764 fdcb 69e9 9913 1554 a427 b1dd 7bed .d..i....T.&#039;..{.
0x0040: 4581 093c 0c86 3b68 671d 0b01 31ea 158b E..&lt;..;hg...1...
0x0050: afe3 f913
ciphertext


# Page. 22

![Page Image](https://bcdn.docswell.com/page/9J29DW5VER.jpg)

Technical Challenge 1 — What cipher can you actually use?
●
`bpf_crypto_ctx_create(type=&quot;skcipher&quot;, algo=&quot;ctr(aes)&quot;)` → returns EINVAL
○
Why? bpf_crypto internally calls crypto_alloc_lskcipher() — the newer &quot;linear&quot;
skcipher API
●
○
ctr(aes) is listed in /proc/crypto as skcipher, but has no lskcipher implementation
○
/proc/crypto lies to you. Only ecb(aes) and cbc(aes) actually work.
○
This is completely undocumented
We ended up on cbc(aes) — IV-based block chaining, avoids ECB&#039;s
deterministic-block weakness
●
Also: IV must live in map memory, not on the stack — verifier rejects frame pointer for
bpf_dynptr_from_mem
[1] https://cocalc.com/github/torvalds/linux/blob/master/tools/testing/selftests/bpf/progs/crypto_bench.c


# Page. 23

![Page Image](https://bcdn.docswell.com/page/DEY42LKQJM.jpg)

Technical Challenge 2 — The veriﬁer battle
●
// ❌ Verifier sees payload_len could be 0, then negative
●
payload_len = skb→len - 34;
●
payload_len &amp; 15u;
●
// ✅ One mask to rule them all
●
payload_len = (skb→len - HDR_LEN &amp; 0x7f0;
●
`0x7f0` does 3 things in 1 instruction:
●
Aligns to 16 bytes (low 4 bits cleared)
●
Caps at 2032 (only bits 410 survive)
●
Forces non-negative (result always 0, 2032
●
Takeaway: bound with AND, not with if-else. The verifier trusts bitmasks completely.
[1] https://cocalc.com/github/torvalds/linux/blob/master/tools/testing/selftests/bpf/progs/crypto_bench.c


# Page. 24

![Page Image](https://bcdn.docswell.com/page/VJNY84R278.jpg)

Technical Challenge 2 — The veriﬁer battle (by Claude)
Prompt Claude Code to compile the BPF object and test-load with bpftool to
see verifier output without going through the full Go loader:
[1] https://cocalc.com/github/torvalds/linux/blob/master/tools/testing/selftests/bpf/progs/crypto_bench.c


# Page. 25

![Page Image](https://bcdn.docswell.com/page/YE9PKQMDJ3.jpg)

Technical Challenge 3 — The scratch-bounce pattern
●
You&#039;d expect: create a dynptr pointing at skb payload offset 34, encrypt
in-place
●
You can&#039;t. bpf_dynptr_from_skb = whole packet, bpf_dynptr_from_mem =
map/stack only
○
admitted by commiter 2
●
skb → load_bytes → scratch.src → encrypt → scratch.dst → store_bytes → skb
●
Two full memcpy per packet, per direction — this is the biggest performance
cost
[1] https://cocalc.com/github/torvalds/linux/blob/master/tools/testing/selftests/bpf/progs/crypto_bench.c
[2] https://lore.kernel.org/bpf/b3f421b5-e9e2-4719-8529-56f2a0cf1cdf@linux.dev/T/


# Page. 26

![Page Image](https://bcdn.docswell.com/page/GE8DRGL5ED.jpg)

AEAD was designed for but never implemented?
● kernel/bpf/crypto.c [1]
○ @authsize: The length of authentication tag used by algorithm.
[1] https://github.com/torvalds/linux/blob/2d1373e4246da3b58e1df058374ed6b101804e07/kernel/bpf/crypto.c


# Page. 27

![Page Image](https://bcdn.docswell.com/page/LELMKGL37R.jpg)

What Would bpf_crypto_aead Unlock?
●
Full ChaCha20Poly1305 in TC/XDP  WireGuard-compatible
data plane becomes feasible
●
The framework is ready
○
authsize field, setauthsize callback, pluggable crypto type
registration all exist.
●
We still have technical challenges for production-ready quality 😭
○
Key management
○
Nonce management: atomically increment packet counter
○
wireguard packet framing within eBPF program


# Page. 28

![Page Image](https://bcdn.docswell.com/page/4JMY2QMMJW.jpg)

Takeaways &amp; Future
●
bpf_crypto kfuncs work for symmetric stream encryption of real packets
in TC programs.
●
AEAD is the missing piece for security-critical protocols like WireGuard.
●
The gap between &quot;designed for&quot; and &quot;implemented&quot; is an opportunity
for upstream contribution.
○
“Feel free to add them, btw.ˮ


# Page. 29

![Page Image](https://bcdn.docswell.com/page/PJR9M8VL79.jpg)

Thank You / Q&amp;A
Code:
https://github.com/NT-marlowe/wireguard-bpf-poc


# Page. 30

![Page Image](https://bcdn.docswell.com/page/PEXQV8W6JX.jpg)

Appendix


