mirror of
https://github.com/eunomia-bpf/bpf-developer-tutorial.git
synced 2026-10-29 03:34:44 +08:00
fix: capture the actual TCP header length
This commit is contained in:
@@ -18,12 +18,24 @@ XDP is a high-performance data path within the Linux kernel that allows for prog
|
|||||||
|
|
||||||
Let's dive into the kernel-space eBPF code that captures TCP header information.
|
Let's dive into the kernel-space eBPF code that captures TCP header information.
|
||||||
|
|
||||||
|
The kernel and user-space programs share the event definition in `xdp-tcpdump.h`. A TCP header can contain 20 to 60 bytes, and `header_len` records the exact size derived from the packet's data-offset field.
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define MAX_TCP_HEADER_BYTES 60
|
||||||
|
|
||||||
|
struct tcp_event {
|
||||||
|
unsigned int header_len;
|
||||||
|
unsigned char header[MAX_TCP_HEADER_BYTES];
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
### Full Kernel Code
|
### Full Kernel Code
|
||||||
|
|
||||||
```c
|
```c
|
||||||
#include "vmlinux.h"
|
#include "vmlinux.h"
|
||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include <bpf/bpf_endian.h>
|
#include <bpf/bpf_endian.h>
|
||||||
|
#include "xdp-tcpdump.h"
|
||||||
|
|
||||||
#define ETH_P_IP 0x0800
|
#define ETH_P_IP 0x0800
|
||||||
|
|
||||||
@@ -94,29 +106,38 @@ int xdp_pass(struct xdp_md *ctx)
|
|||||||
return XDP_PASS;
|
return XDP_PASS;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define the number of bytes you want to capture from the TCP header
|
// Derive the TCP header length from data offset (doff), measured in 32-bit words
|
||||||
const int tcp_header_bytes = 32;
|
__u32 tcp_header_bytes = tcp->doff * 4;
|
||||||
|
if (tcp_header_bytes < sizeof(*tcp) || tcp_header_bytes > MAX_TCP_HEADER_BYTES) {
|
||||||
// Ensure that the desired number of bytes does not exceed packet bounds
|
|
||||||
if ((void *)tcp + tcp_header_bytes > data_end) {
|
|
||||||
return XDP_PASS;
|
return XDP_PASS;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reserve space in the ring buffer
|
// Reserve a fixed-size event because bpf_ringbuf_reserve requires a constant size
|
||||||
void *ringbuf_space = bpf_ringbuf_reserve(&rb, tcp_header_bytes, 0);
|
struct tcp_event *event = bpf_ringbuf_reserve(&rb, sizeof(*event), 0);
|
||||||
if (!ringbuf_space) {
|
if (!event) {
|
||||||
return XDP_PASS; // If reservation fails, skip processing
|
return XDP_PASS; // If reservation fails, skip processing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event->header_len = tcp_header_bytes;
|
||||||
|
__builtin_memset(event->header, 0, sizeof(event->header));
|
||||||
|
|
||||||
// Copy the TCP header bytes into the ring buffer
|
// Copy the TCP header bytes into the ring buffer
|
||||||
// Using a loop to ensure compliance with eBPF verifier
|
// Using a loop to ensure compliance with eBPF verifier
|
||||||
for (int i = 0; i < tcp_header_bytes; i++) {
|
for (int i = 0; i < MAX_TCP_HEADER_BYTES; i++) {
|
||||||
|
if (i >= tcp_header_bytes)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if ((void *)tcp + i + 1 > data_end) {
|
||||||
|
bpf_ringbuf_discard(event, 0);
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned char byte = *((unsigned char *)tcp + i);
|
unsigned char byte = *((unsigned char *)tcp + i);
|
||||||
((unsigned char *)ringbuf_space)[i] = byte;
|
event->header[i] = byte;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Submit the data to the ring buffer
|
// Submit the data to the ring buffer
|
||||||
bpf_ringbuf_submit(ringbuf_space, 0);
|
bpf_ringbuf_submit(event, 0);
|
||||||
|
|
||||||
// Optional: Print a debug message
|
// Optional: Print a debug message
|
||||||
bpf_printk("Captured TCP header (%d bytes)", tcp_header_bytes);
|
bpf_printk("Captured TCP header (%d bytes)", tcp_header_bytes);
|
||||||
@@ -156,26 +177,37 @@ static bool is_tcp(struct ethhdr *eth, void *data_end)
|
|||||||
In the `xdp_pass` function, we:
|
In the `xdp_pass` function, we:
|
||||||
|
|
||||||
1. Parse the Ethernet, IP, and TCP headers.
|
1. Parse the Ethernet, IP, and TCP headers.
|
||||||
2. Ensure all headers are within the packet bounds to prevent invalid memory access.
|
2. Read and validate the TCP data-offset field, which gives the actual header length.
|
||||||
3. Reserve space in the ring buffer to store the TCP header.
|
3. Reserve a fixed-size event in the ring buffer because the helper requires a compile-time constant size.
|
||||||
4. Copy the TCP header bytes into the ring buffer.
|
4. Copy only the validated header bytes into the event while checking each packet access.
|
||||||
5. Submit the data to the ring buffer for user-space consumption.
|
5. Submit the data to the ring buffer for user-space consumption.
|
||||||
|
|
||||||
```c
|
```c
|
||||||
// Reserve space in the ring buffer
|
__u32 tcp_header_bytes = tcp->doff * 4;
|
||||||
void *ringbuf_space = bpf_ringbuf_reserve(&rb, tcp_header_bytes, 0);
|
if (tcp_header_bytes < sizeof(*tcp) || tcp_header_bytes > MAX_TCP_HEADER_BYTES) {
|
||||||
if (!ringbuf_space) {
|
|
||||||
return XDP_PASS;
|
return XDP_PASS;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the TCP header bytes
|
struct tcp_event *event = bpf_ringbuf_reserve(&rb, sizeof(*event), 0);
|
||||||
for (int i = 0; i < tcp_header_bytes; i++) {
|
if (!event) {
|
||||||
unsigned char byte = *((unsigned char *)tcp + i);
|
return XDP_PASS;
|
||||||
((unsigned char *)ringbuf_space)[i] = byte;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Submit to ring buffer
|
event->header_len = tcp_header_bytes;
|
||||||
bpf_ringbuf_submit(ringbuf_space, 0);
|
__builtin_memset(event->header, 0, sizeof(event->header));
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_TCP_HEADER_BYTES; i++) {
|
||||||
|
if (i >= tcp_header_bytes)
|
||||||
|
break;
|
||||||
|
if ((void *)tcp + i + 1 > data_end) {
|
||||||
|
bpf_ringbuf_discard(event, 0);
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
unsigned char byte = *((unsigned char *)tcp + i);
|
||||||
|
event->header[i] = byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
bpf_ringbuf_submit(event, 0);
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Using bpf_printk for Debugging
|
#### Using bpf_printk for Debugging
|
||||||
@@ -199,17 +231,25 @@ Let's examine the user-space program that reads the captured TCP headers from th
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
#include <bpf/libbpf.h>
|
#include <bpf/libbpf.h>
|
||||||
#include <bpf/bpf.h>
|
#include <bpf/bpf.h>
|
||||||
|
|
||||||
#include "xdp-tcpdump.skel.h" // Generated skeleton header
|
#include "xdp-tcpdump.skel.h" // Generated skeleton header
|
||||||
|
#include "xdp-tcpdump.h"
|
||||||
|
|
||||||
// Callback function to handle events from the ring buffer
|
// Callback function to handle events from the ring buffer
|
||||||
static int handle_event(void *ctx, void *data, size_t data_sz)
|
static int handle_event(void *ctx, void *data, size_t data_sz)
|
||||||
{
|
{
|
||||||
if (data_sz < 20) { // Minimum TCP header size
|
if (data_sz < sizeof(struct tcp_event)) {
|
||||||
fprintf(stderr, "Received incomplete TCP header\n");
|
fprintf(stderr, "Received incomplete TCP event\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct tcp_event *event = data;
|
||||||
|
if (event->header_len < 20 || event->header_len > MAX_TCP_HEADER_BYTES) {
|
||||||
|
fprintf(stderr, "Invalid TCP header length: %u\n", event->header_len);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,12 +275,7 @@ static int handle_event(void *ctx, void *data, size_t data_sz)
|
|||||||
// Options and padding may follow
|
// Options and padding may follow
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
if (data_sz < sizeof(struct tcphdr)) {
|
struct tcphdr *tcp = (struct tcphdr *)event->header;
|
||||||
fprintf(stderr, "Data size (%zu) less than TCP header size\n", data_sz);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct tcphdr *tcp = (struct tcphdr *)data;
|
|
||||||
|
|
||||||
// Convert fields from network byte order to host byte order
|
// Convert fields from network byte order to host byte order
|
||||||
uint16_t source_port = ntohs(tcp->source);
|
uint16_t source_port = ntohs(tcp->source);
|
||||||
@@ -369,13 +404,17 @@ The `handle_event` function processes TCP header data received from the ring buf
|
|||||||
```c
|
```c
|
||||||
static int handle_event(void *ctx, void *data, size_t data_sz)
|
static int handle_event(void *ctx, void *data, size_t data_sz)
|
||||||
{
|
{
|
||||||
// Validate data size
|
if (data_sz < sizeof(struct tcp_event)) {
|
||||||
if (data_sz < 20) {
|
fprintf(stderr, "Received incomplete TCP event\n");
|
||||||
fprintf(stderr, "Received incomplete TCP header\n");
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct tcp_event *event = data;
|
||||||
|
if (event->header_len < 20 || event->header_len > MAX_TCP_HEADER_BYTES) {
|
||||||
|
fprintf(stderr, "Invalid TCP header length: %u\n", event->header_len);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the TCP header
|
|
||||||
// ... (parsing code)
|
// ... (parsing code)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -16,12 +16,24 @@ XDP 是 Linux 内核中一个高性能的数据路径,允许在网络栈的最
|
|||||||
|
|
||||||
让我们深入了解捕获 TCP 头信息的内核空间 eBPF 代码。
|
让我们深入了解捕获 TCP 头信息的内核空间 eBPF 代码。
|
||||||
|
|
||||||
|
内核程序与用户空间程序通过 `xdp-tcpdump.h` 共享事件定义。TCP 头的长度可以是 20 到 60 字节,`header_len` 保存根据数据偏移字段计算出的准确长度。
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define MAX_TCP_HEADER_BYTES 60
|
||||||
|
|
||||||
|
struct tcp_event {
|
||||||
|
unsigned int header_len;
|
||||||
|
unsigned char header[MAX_TCP_HEADER_BYTES];
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
### 完整的内核代码
|
### 完整的内核代码
|
||||||
|
|
||||||
```c
|
```c
|
||||||
#include "vmlinux.h"
|
#include "vmlinux.h"
|
||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include <bpf/bpf_endian.h>
|
#include <bpf/bpf_endian.h>
|
||||||
|
#include "xdp-tcpdump.h"
|
||||||
|
|
||||||
#define ETH_P_IP 0x0800
|
#define ETH_P_IP 0x0800
|
||||||
|
|
||||||
@@ -92,29 +104,38 @@ int xdp_pass(struct xdp_md *ctx)
|
|||||||
return XDP_PASS;
|
return XDP_PASS;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 定义要捕获的 TCP 头字节数
|
// 根据以 32 位字为单位的数据偏移字段 doff 计算 TCP 头长度
|
||||||
const int tcp_header_bytes = 32;
|
__u32 tcp_header_bytes = tcp->doff * 4;
|
||||||
|
if (tcp_header_bytes < sizeof(*tcp) || tcp_header_bytes > MAX_TCP_HEADER_BYTES) {
|
||||||
// 确保所需字节数不超过数据包边界
|
|
||||||
if ((void *)tcp + tcp_header_bytes > data_end) {
|
|
||||||
return XDP_PASS;
|
return XDP_PASS;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 在环形缓冲区中预留空间
|
// bpf_ringbuf_reserve 要求大小为常量,因此预留固定大小的事件
|
||||||
void *ringbuf_space = bpf_ringbuf_reserve(&rb, tcp_header_bytes, 0);
|
struct tcp_event *event = bpf_ringbuf_reserve(&rb, sizeof(*event), 0);
|
||||||
if (!ringbuf_space) {
|
if (!event) {
|
||||||
return XDP_PASS; // 如果预留失败,跳过处理
|
return XDP_PASS; // 如果预留失败,跳过处理
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event->header_len = tcp_header_bytes;
|
||||||
|
__builtin_memset(event->header, 0, sizeof(event->header));
|
||||||
|
|
||||||
// 将 TCP 头字节复制到环形缓冲区
|
// 将 TCP 头字节复制到环形缓冲区
|
||||||
// 使用循环以确保符合 eBPF 验证器要求
|
// 使用循环以确保符合 eBPF 验证器要求
|
||||||
for (int i = 0; i < tcp_header_bytes; i++) {
|
for (int i = 0; i < MAX_TCP_HEADER_BYTES; i++) {
|
||||||
|
if (i >= tcp_header_bytes)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if ((void *)tcp + i + 1 > data_end) {
|
||||||
|
bpf_ringbuf_discard(event, 0);
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned char byte = *((unsigned char *)tcp + i);
|
unsigned char byte = *((unsigned char *)tcp + i);
|
||||||
((unsigned char *)ringbuf_space)[i] = byte;
|
event->header[i] = byte;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将数据提交到环形缓冲区
|
// 将数据提交到环形缓冲区
|
||||||
bpf_ringbuf_submit(ringbuf_space, 0);
|
bpf_ringbuf_submit(event, 0);
|
||||||
|
|
||||||
// 可选:打印调试信息
|
// 可选:打印调试信息
|
||||||
bpf_printk("Captured TCP header (%d bytes)", tcp_header_bytes);
|
bpf_printk("Captured TCP header (%d bytes)", tcp_header_bytes);
|
||||||
@@ -154,26 +175,37 @@ static bool is_tcp(struct ethhdr *eth, void *data_end)
|
|||||||
在 `xdp_pass` 函数中,我们:
|
在 `xdp_pass` 函数中,我们:
|
||||||
|
|
||||||
1. 解析以太网、IP 和 TCP 头。
|
1. 解析以太网、IP 和 TCP 头。
|
||||||
2. 确保所有头信息在数据包边界内,以防止无效内存访问。
|
2. 读取并校验 TCP 数据偏移字段,得到实际的头长度。
|
||||||
3. 在环形缓冲区中预留空间以存储 TCP 头。
|
3. 由于辅助函数要求使用编译期常量大小,在环形缓冲区中预留固定大小的事件。
|
||||||
4. 将 TCP 头字节复制到环形缓冲区。
|
4. 逐次检查数据包边界,只把经过校验的头字节复制到事件中。
|
||||||
5. 提交数据到环形缓冲区,供用户空间使用。
|
5. 提交数据到环形缓冲区,供用户空间使用。
|
||||||
|
|
||||||
```c
|
```c
|
||||||
// 在环形缓冲区中预留空间
|
__u32 tcp_header_bytes = tcp->doff * 4;
|
||||||
void *ringbuf_space = bpf_ringbuf_reserve(&rb, tcp_header_bytes, 0);
|
if (tcp_header_bytes < sizeof(*tcp) || tcp_header_bytes > MAX_TCP_HEADER_BYTES) {
|
||||||
if (!ringbuf_space) {
|
|
||||||
return XDP_PASS;
|
return XDP_PASS;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 复制 TCP 头字节
|
struct tcp_event *event = bpf_ringbuf_reserve(&rb, sizeof(*event), 0);
|
||||||
for (int i = 0; i < tcp_header_bytes; i++) {
|
if (!event) {
|
||||||
unsigned char byte = *((unsigned char *)tcp + i);
|
return XDP_PASS;
|
||||||
((unsigned char *)ringbuf_space)[i] = byte;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提交到环形缓冲区
|
event->header_len = tcp_header_bytes;
|
||||||
bpf_ringbuf_submit(ringbuf_space, 0);
|
__builtin_memset(event->header, 0, sizeof(event->header));
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_TCP_HEADER_BYTES; i++) {
|
||||||
|
if (i >= tcp_header_bytes)
|
||||||
|
break;
|
||||||
|
if ((void *)tcp + i + 1 > data_end) {
|
||||||
|
bpf_ringbuf_discard(event, 0);
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
unsigned char byte = *((unsigned char *)tcp + i);
|
||||||
|
event->header[i] = byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
bpf_ringbuf_submit(event, 0);
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 使用 bpf_printk 进行调试
|
#### 使用 bpf_printk 进行调试
|
||||||
@@ -197,17 +229,25 @@ bpf_printk("Captured TCP header (%d bytes)", tcp_header_bytes);
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
#include <bpf/libbpf.h>
|
#include <bpf/libbpf.h>
|
||||||
#include <bpf/bpf.h>
|
#include <bpf/bpf.h>
|
||||||
|
|
||||||
#include "xdp-tcpdump.skel.h" // 生成的骨架头文件
|
#include "xdp-tcpdump.skel.h" // 生成的骨架头文件
|
||||||
|
#include "xdp-tcpdump.h"
|
||||||
|
|
||||||
// 处理环形缓冲区事件的回调函数
|
// 处理环形缓冲区事件的回调函数
|
||||||
static int handle_event(void *ctx, void *data, size_t data_sz)
|
static int handle_event(void *ctx, void *data, size_t data_sz)
|
||||||
{
|
{
|
||||||
if (data_sz < 20) { // 最小 TCP 头大小
|
if (data_sz < sizeof(struct tcp_event)) {
|
||||||
fprintf(stderr, "Received incomplete TCP header\n");
|
fprintf(stderr, "Received incomplete TCP event\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct tcp_event *event = data;
|
||||||
|
if (event->header_len < 20 || event->header_len > MAX_TCP_HEADER_BYTES) {
|
||||||
|
fprintf(stderr, "Invalid TCP header length: %u\n", event->header_len);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,12 +273,7 @@ static int handle_event(void *ctx, void *data, size_t data_sz)
|
|||||||
// 可能还有选项和填充
|
// 可能还有选项和填充
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
if (data_sz < sizeof(struct tcphdr)) {
|
struct tcphdr *tcp = (struct tcphdr *)event->header;
|
||||||
fprintf(stderr, "Data size (%zu) less than TCP header size\n", data_sz);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct tcphdr *tcp = (struct tcphdr *)data;
|
|
||||||
|
|
||||||
// 将字段从网络字节序转换为主机字节序
|
// 将字段从网络字节序转换为主机字节序
|
||||||
uint16_t source_port = ntohs(tcp->source);
|
uint16_t source_port = ntohs(tcp->source);
|
||||||
@@ -367,13 +402,17 @@ cleanup:
|
|||||||
```c
|
```c
|
||||||
static int handle_event(void *ctx, void *data, size_t data_sz)
|
static int handle_event(void *ctx, void *data, size_t data_sz)
|
||||||
{
|
{
|
||||||
// 验证数据大小
|
if (data_sz < sizeof(struct tcp_event)) {
|
||||||
if (data_sz < 20) {
|
fprintf(stderr, "Received incomplete TCP event\n");
|
||||||
fprintf(stderr, "Received incomplete TCP header\n");
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct tcp_event *event = data;
|
||||||
|
if (event->header_len < 20 || event->header_len > MAX_TCP_HEADER_BYTES) {
|
||||||
|
fprintf(stderr, "Invalid TCP header length: %u\n", event->header_len);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析 TCP 头
|
|
||||||
// ...(解析代码)
|
// ...(解析代码)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -506,4 +545,4 @@ Captured TCP Header:
|
|||||||
|
|
||||||
如果您想了解更多关于 eBPF 的内容,请访问我们的教程代码仓库 <https://github.com/eunomia-bpf/bpf-developer-tutorial> 或我们的网站 <https://eunomia.dev/tutorials/>
|
如果您想了解更多关于 eBPF 的内容,请访问我们的教程代码仓库 <https://github.com/eunomia-bpf/bpf-developer-tutorial> 或我们的网站 <https://eunomia.dev/tutorials/>
|
||||||
|
|
||||||
编程愉快!
|
编程愉快!
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "vmlinux.h"
|
#include "vmlinux.h"
|
||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include <bpf/bpf_endian.h>
|
#include <bpf/bpf_endian.h>
|
||||||
|
#include "xdp-tcpdump.h"
|
||||||
|
|
||||||
#define ETH_P_IP 0x0800
|
#define ETH_P_IP 0x0800
|
||||||
|
|
||||||
@@ -71,32 +72,39 @@ int xdp_pass(struct xdp_md *ctx)
|
|||||||
return XDP_PASS;
|
return XDP_PASS;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define the number of bytes you want to capture from the TCP header
|
// Derive the TCP header length from data offset (doff), measured in 32-bit words
|
||||||
// Typically, the TCP header is 20 bytes, but with options, it can be longer
|
__u32 tcp_header_bytes = tcp->doff * 4;
|
||||||
// Here, we'll capture the first 32 bytes to include possible options
|
if (tcp_header_bytes < sizeof(*tcp) || tcp_header_bytes > MAX_TCP_HEADER_BYTES) {
|
||||||
const int tcp_header_bytes = 32;
|
|
||||||
|
|
||||||
// Ensure that the desired number of bytes does not exceed packet bounds
|
|
||||||
if ((void *)tcp + tcp_header_bytes > data_end) {
|
|
||||||
return XDP_PASS;
|
return XDP_PASS;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reserve space in the ring buffer
|
// Reserve a fixed-size event because bpf_ringbuf_reserve requires a constant size
|
||||||
void *ringbuf_space = bpf_ringbuf_reserve(&rb, tcp_header_bytes, 0);
|
struct tcp_event *event = bpf_ringbuf_reserve(&rb, sizeof(*event), 0);
|
||||||
if (!ringbuf_space) {
|
if (!event) {
|
||||||
return XDP_PASS; // If reservation fails, skip processing
|
return XDP_PASS; // If reservation fails, skip processing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event->header_len = tcp_header_bytes;
|
||||||
|
__builtin_memset(event->header, 0, sizeof(event->header));
|
||||||
|
|
||||||
// Copy the TCP header bytes into the ring buffer
|
// Copy the TCP header bytes into the ring buffer
|
||||||
// Using a loop to ensure compliance with eBPF verifier
|
// Using a loop to ensure compliance with eBPF verifier
|
||||||
for (int i = 0; i < tcp_header_bytes; i++) {
|
for (int i = 0; i < MAX_TCP_HEADER_BYTES; i++) {
|
||||||
|
if (i >= tcp_header_bytes)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if ((void *)tcp + i + 1 > data_end) {
|
||||||
|
bpf_ringbuf_discard(event, 0);
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
// Accessing each byte safely within bounds
|
// Accessing each byte safely within bounds
|
||||||
unsigned char byte = *((unsigned char *)tcp + i);
|
unsigned char byte = *((unsigned char *)tcp + i);
|
||||||
((unsigned char *)ringbuf_space)[i] = byte;
|
event->header[i] = byte;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Submit the data to the ring buffer
|
// Submit the data to the ring buffer
|
||||||
bpf_ringbuf_submit(ringbuf_space, 0);
|
bpf_ringbuf_submit(event, 0);
|
||||||
|
|
||||||
// Optional: Print a debug message (will appear in kernel logs)
|
// Optional: Print a debug message (will appear in kernel logs)
|
||||||
bpf_printk("Captured TCP header (%d bytes)", tcp_header_bytes);
|
bpf_printk("Captured TCP header (%d bytes)", tcp_header_bytes);
|
||||||
|
|||||||
@@ -10,12 +10,19 @@
|
|||||||
#include <bpf/bpf.h>
|
#include <bpf/bpf.h>
|
||||||
|
|
||||||
#include "xdp-tcpdump.skel.h" // Generated skeleton header
|
#include "xdp-tcpdump.skel.h" // Generated skeleton header
|
||||||
|
#include "xdp-tcpdump.h"
|
||||||
|
|
||||||
// Callback function to handle events from the ring buffer
|
// Callback function to handle events from the ring buffer
|
||||||
static int handle_event(void *ctx, void *data, size_t data_sz)
|
static int handle_event(void *ctx, void *data, size_t data_sz)
|
||||||
{
|
{
|
||||||
if (data_sz < 20) { // Minimum TCP header size
|
if (data_sz < sizeof(struct tcp_event)) {
|
||||||
fprintf(stderr, "Received incomplete TCP header\n");
|
fprintf(stderr, "Received incomplete TCP event\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct tcp_event *event = data;
|
||||||
|
if (event->header_len < 20 || event->header_len > MAX_TCP_HEADER_BYTES) {
|
||||||
|
fprintf(stderr, "Invalid TCP header length: %u\n", event->header_len);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,12 +48,7 @@ static int handle_event(void *ctx, void *data, size_t data_sz)
|
|||||||
// Options and padding may follow
|
// Options and padding may follow
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
if (data_sz < sizeof(struct tcphdr)) {
|
struct tcphdr *tcp = (struct tcphdr *)event->header;
|
||||||
fprintf(stderr, "Data size (%zu) less than TCP header size\n", data_sz);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct tcphdr *tcp = (struct tcphdr *)data;
|
|
||||||
|
|
||||||
// Convert fields from network byte order to host byte order
|
// Convert fields from network byte order to host byte order
|
||||||
uint16_t source_port = ntohs(tcp->source);
|
uint16_t source_port = ntohs(tcp->source);
|
||||||
|
|||||||
11
src/41-xdp-tcpdump/xdp-tcpdump.h
Normal file
11
src/41-xdp-tcpdump/xdp-tcpdump.h
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#ifndef __XDP_TCPDUMP_H
|
||||||
|
#define __XDP_TCPDUMP_H
|
||||||
|
|
||||||
|
#define MAX_TCP_HEADER_BYTES 60
|
||||||
|
|
||||||
|
struct tcp_event {
|
||||||
|
unsigned int header_len;
|
||||||
|
unsigned char header[MAX_TCP_HEADER_BYTES];
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* __XDP_TCPDUMP_H */
|
||||||
Reference in New Issue
Block a user