1. eBPFとは何ですか?
eBPF (拡張 Berkeley Packet Filter) は、カーネル ソース コードを変更したり、カーネル モジュールをロードしたりせずに、カーネル内でサンドボックス プログラムを実行できるようにする Linux カーネル サブシステムです。プログラムは実行前にカーネルバイトコードベリファイアによって検証され、安全性が確保されます。
ネットワーキングの場合、eBPF プログラムはフックポイントカーネルのネットワーク スタック内に存在し、パケットを検査、変更、リダイレクト、またはドロップできます。主な利点iptablesカーネル モジュールはパフォーマンスとプログラマビリティです。eBPF プログラムはネイティブ コードに JIT コンパイルされ、状態を共有できます。地図(キーと値のストアはカーネルとユーザー空間の間で共有されます)。
| フック | 位置 | レイテンシ | 使用事例 |
|---|---|---|---|
| XDP | NIC ドライバー、sk_buff 割り当て前 | 最低 | DDoS ドロップ、ロード バランシング |
| TC 入力/出力 | sk_buff割り当て後 | 低い | トラフィックシェーピング、マーキング、リダイレクト |
| ソケットフィルター | ソケット受信パス | 中くらい | tcpdump スタイルのフィルタリング |
| kプローブ/トレースポイント | カーネル関数の入口/出口 | さまざま | 可観測性、追跡性 |
2. XDP フックポイント
XDP (eXpress Data Path) プログラムは、カーネルがネットワーク スタックを割り当てる前の、NIC ドライバー内で、ネットワーク スタックの可能な限り早い時点で実行されます。sk_buff。これはつまり:
- ネイティブXDP: ドライバーは XDP をネイティブにサポートします (Intel i40e、Mellanox mlx5 など)。最速 — ドライバーコンテキストで実行されます。
- 汎用 XDP: ネイティブ サポートのないドライバーのフォールバック。追いかける
sk_buff割り当て — それでも iptables より高速ですが、ネイティブほど高速ではありません。 - オフロードされた XDP: プログラムは NIC ASIC 自体で実行されます。 SmartNIC ハードウェア (Netronome など) が必要です。 CPU コストゼロ。
XDP プログラムは、次の 5 つの判定のいずれかを返します。
| リターンコード | アクション |
|---|---|
XDP_DROP | パケットを即座にドロップ - 遅延が最も短い破棄 |
XDP_PASS | 通常のネットワークスタックにパスアップ |
XDP_TX | 同じインターフェイスを送信し返す (バウンス) |
XDP_REDIRECT | 別のインターフェイスまたは AF_XDP ソケットにリダイレクトします |
XDP_ABORTED | エラー パス - トレース イベントで削除 |
3. XDP パケットドロップの例
次のプログラムは、eBPF マップに保存されているソース IP からのすべての UDP パケットをドロップし、ユーザー空間のコントロール プレーンが実行時にブロックリストを更新できるようにします。
// xdp_drop_udp.c — Drop UDP from IPs in a BPF map
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <bpf/bpf_helpers.h>
// BPF map: src IP → drop flag (1 = drop)
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1024);
__type(key, __u32); // source IPv4 address
__type(value, __u32); // 1 = block
} blocklist SEC(".maps");
SEC("xdp")
int xdp_drop_udp(struct xdp_md *ctx) {
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;
// Parse Ethernet header
struct ethhdr *eth = data;
if ((void *)(eth + 1) > data_end) return XDP_PASS;
if (eth->h_proto != __constant_htons(ETH_P_IP)) return XDP_PASS;
// Parse IPv4 header
struct iphdr *ip = (void *)(eth + 1);
if ((void *)(ip + 1) > data_end) return XDP_PASS;
if (ip->protocol != IPPROTO_UDP) return XDP_PASS;
// Check blocklist map
__u32 src = ip->saddr;
__u32 *val = bpf_map_lookup_elem(&blocklist, &src);
if (val && *val == 1) return XDP_DROP;
return XDP_PASS;
}
char _license[] SEC("license") = "GPL";
data_end。すべてのポインター算術演算の後に境界チェックを行う必要があります。そうしないと、プログラムはロードされません。をロードしてアタッチしますip:
# Compile
clang -O2 -target bpf -c xdp_drop_udp.c -o xdp_drop_udp.o
# Attach to interface (native XDP)
ip link set eth0 xdp obj xdp_drop_udp.o sec xdp
# Add an IP to the blocklist via bpftool
bpftool map update name blocklist key 0x01 0x02 0x03 0x04 value 0x01 0x00 0x00 0x00
# Remove XDP program
ip link set eth0 xdp off
4. AF_XDP: カーネルバイパス
AF_XDPXDP と組み合わせたソケット ファミリです。XDP_REDIRECT判定により、パケットごとにカーネルが関与することなく、パケットをユーザー空間メモリ領域 (UMEM) に直接配信します。これは、DPDK のカーネル バイパス モデルに対する eBPF エコシステムの答えです。
主要なコンポーネント:
- UMEM: フレームに分割されたユーザー空間に登録されたメモリ領域。共有メモリを介してカーネルとユーザー空間の間で共有されます。
- 指輪: ソケットごとに 4 つのロックフリー リング: フィル (ユーザー空間 → 空きフレームのあるカーネル)、完了 (カーネル → ユーザー空間のある TX 完了フレーム)、RX リング (カーネル → ユーザー空間のある受信フレーム)、TX リング (ユーザー空間 → 送信フレームのあるカーネル)。
- ゼロコピーモード: ドライバーがサポートしている場合、フレームはコピーなしで転送されます。単にポインターを渡すだけです。
AF_XDP は、DPDK の複雑な操作を必要とせずに、ライン レートでのカスタム パケット処理に最適です (基本的な使用に必要な巨大ページや CPU ピン接続はありません)。
5. tc BPF: トラフィック シェーピングとフィルタリング
tc(トラフィック制御) BPF プログラムは、clsactqdisc であり、イングレスまたはエグレスで実行できます。 XDP とは異なり、完全に表示されます。sk_buffソケットのメタデータ、VLAN、トンネル ヘッダーにアクセスできます。
// tc_mark.c — Mark packets with DSCP EF (46) for VoIP traffic on port 5060
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <bpf/bpf_helpers.h>
SEC("classifier")
int tc_mark_voip(struct __sk_buff *skb) {
void *data = (void *)(long)skb->data;
void *data_end = (void *)(long)skb->data_end;
struct ethhdr *eth = data;
if ((void *)(eth + 1) > data_end) return TC_ACT_OK;
if (eth->h_proto != __constant_htons(ETH_P_IP)) return TC_ACT_OK;
struct iphdr *ip = (void *)(eth + 1);
if ((void *)(ip + 1) > data_end) return TC_ACT_OK;
if (ip->protocol != IPPROTO_UDP) return TC_ACT_OK;
struct udphdr *udp = (void *)(ip + 1);
if ((void *)(udp + 1) > data_end) return TC_ACT_OK;
// Mark SIP traffic (port 5060) with DSCP EF (46 = 0xB8 in TOS byte)
if (udp->dest == __constant_htons(5060) || udp->source == __constant_htons(5060)) {
// DSCP EF = 46, shifted left 2 bits in TOS field = 184 (0xB8)
bpf_skb_store_bytes(skb, offsetof(struct iphdr, tos) + sizeof(struct ethhdr),
&((__u8){184}), 1, BPF_F_RECOMPUTE_CSUM);
}
return TC_ACT_OK;
}
char _license[] SEC("license") = "GPL";
# Attach tc BPF program
tc qdisc add dev eth0 clsact
tc filter add dev eth0 egress bpf da obj tc_mark.o sec classifier
6. eBPF マップによるレート制限
eBPF マップによりステートフル処理が可能になります。次のパターンは、BPF_MAP_TYPE_LRU_HASH:
// Conceptual token bucket per source IP — checks tokens, drops if exceeded
struct ratelimit_entry {
__u64 tokens; // current token count
__u64 last_update; // nanoseconds timestamp
};
struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__uint(max_entries, 65536);
__type(key, __u32); // source IP
__type(value, struct ratelimit_entry);
} rate_map SEC(".maps");
// In XDP program:
// 1. bpf_ktime_get_ns() — get current time
// 2. Lookup entry for src IP
// 3. Refill tokens: tokens += (elapsed_ns / 1e9) * rate_pps
// 4. If tokens >= 1: decrement and XDP_PASS
// 5. Else: XDP_DROP
7. bpftool および bpftrace イントロスペクション
ライブ eBPF プログラムを操作するための 2 つの重要なツール:
# bpftool — inspect loaded programs and maps
bpftool prog list # list all loaded eBPF programs
bpftool prog show id 42 # details for program ID 42
bpftool prog dump xlated id 42 # disassemble to eBPF bytecode
bpftool prog dump jited id 42 # dump JIT-compiled native code
bpftool map list # list all BPF maps
bpftool map dump name blocklist # dump all entries in map "blocklist"
bpftool map update name blocklist \
key 192 168 1 100 value 1 0 0 0 # add entry (network byte order)
# bpftrace — DTrace-style one-liners for kernel tracing
# Count XDP drops per second
bpftrace -e 'tracepoint:xdp:xdp_exception { @drops[args->action] = count(); } interval:s:1 { print(@drops); clear(@drops); }'
# Trace tcp_retransmit_skb — show retransmit events with comm name
bpftrace -e 'kprobe:tcp_retransmit_skb { printf("%s retransmit\n", comm); }'
# Histogram of packet sizes on eth0
bpftrace -e 'tracepoint:net:netif_receive_skb /args->name == "eth0"/ { @size = hist(args->len); }'
8. 比較: eBPF/XDP、DPDK、RDMA
| 特徴 | eBPF/XDP | DPDK | RDMA |
|---|---|---|---|
| カーネルの関与 | 最小限 (ドライバー内の XDP) | なし(フルバイパス) | なし (RDMA NIC) |
| メモリモデル | 標準 + AF_XDP UMEM | 巨大なページが必要です | 登録されたメモリ領域 |
| 最大スループット | ~100 Gbps ネイティブ XDP | >100 Gbps | 200+ Gbps (インフィニバンド) |
| CPU使用率 | 低 (イベント駆動型) | 高 (ビジーポーリングコア) | ほぼゼロ (オフロード) |
| 運用の複雑さ | 低 — 標準ツール | 高 - 専用コア、巨大ページ | 高 - ファブリック管理 |
| 使用事例 | DDoS 軽減、LB、可観測性 | 仮想ルーター、NFV、パケット生成 | ストレージ (NVMe-oF)、HPC MPI |
| 言語 | 制限C/錆 | C / 錆び | 動詞 API (C) |