XDP 程序未捕获所有入口数据包
XDP program not capturing all ingress packets
以下 XDP 程序不会捕获所有入口 XDP 数据包。我将源 IP 存储在散列中 table 作为键,值作为 IP 被看到的次数。
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <arpa/inet.h>
struct addr_desc_struct {
__u32 src_ip;
};
struct bpf_map_def SEC("maps") addr_map = {
.type = BPF_MAP_TYPE_LRU_HASH,
.key_size = sizeof(struct addr_desc_struct),
.value_size = sizeof(long),
.max_entries = 4096
};
SEC("collect_ips")
int xdp_ip_stats_prog(struct xdp_md *ctx) {
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct ethhdr *eth = data;
struct iphdr *iph = data + sizeof(struct ethhdr);
if (data_end >= (void *) (eth + sizeof(struct ethhdr))) {
if (eth->h_proto == htons(ETH_P_IP)) {
struct addr_desc_struct addr_desc = {.src_ip = iph->saddr};
long init_val = 1;
long *value = bpf_map_lookup_elem(&addr_map, &addr_desc);
if (value) {
__sync_fetch_and_add(value, 1);
} else {
bpf_map_update_elem(&addr_map, &addr_desc, &init_val, BPF_ANY);
}
}
}
return XDP_PASS;
}
char _license[] SEC("license") = "GPL";
设置:
macOS 主机,VirtualBox 来宾 运行 Lubuntu。我在 VM 上创建了一个 'host only adapter'。 VM 和 macOS 在 192.168.56.x 网络上都有一个接口,IP 分别为 192.168.56.102 和 192.168.56.1。此 XDP 程序使用 xdp-loader
程序在 VM 界面上成功加载。
测试 #1:
运行 IP 192.168.56.102 上虚拟机上的 HTTP 服务器。从 macOS 卷曲此 IP。
观察:XDP 程序没有捕获任何数据包。
测试#2:
运行 192.168.56.1 上 macOS 上的 HTTP 服务器。从 VM 卷曲此 IP。
观察:XDP 程序捕获了一些从 macOS 发送到 VM 的数据包。 Wireshark 指示 XDP 应该收到更多数据包。
测试#3:
从 macOS 通过 SSH 连接到 VM 上的 192.168.56.102。
观察:XDP 没有捕获数据包
在所有测试中,我应该会看到 XDP 程序处理的数据包。
根据 Andrew 的评论,这是更新后的函数。主要问题出在 if (data_end >= (void *) (eth + sizeof(struct ethhdr)))
,这会导致数据包过冲。我应该投射到 char *
。单独使用 data
不符合标准,但在 clang 中有效,因为它将字节添加到 void *
,而不是 bytes*sizeof(some pointer)
.
SEC("collect_ips")
int xdp_ip_stats_prog(struct xdp_md *ctx) {
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct ethhdr *eth = data;
struct iphdr *iph = (char *) data + sizeof(struct ethhdr);
// Without adding sizeof(struct iphdr) results in xdp-loader complaining about "invalid access to packet"
if (data_end >= (void *) ((char *) data + sizeof(struct ethhdr) + sizeof(struct iphdr))) {
if (eth->h_proto == htons(ETH_P_IP)) {
struct addr_desc_struct addr_desc = {.src_ip = iph->saddr};
long init_val = 1;
long *value = bpf_map_lookup_elem(&addr_map, &addr_desc);
if (value) {
__sync_fetch_and_add(value, 1);
} else {
bpf_map_update_elem(&addr_map, &addr_desc, &init_val, BPF_ANY);
}
}
}
return XDP_PASS;
}
以下 XDP 程序不会捕获所有入口 XDP 数据包。我将源 IP 存储在散列中 table 作为键,值作为 IP 被看到的次数。
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <arpa/inet.h>
struct addr_desc_struct {
__u32 src_ip;
};
struct bpf_map_def SEC("maps") addr_map = {
.type = BPF_MAP_TYPE_LRU_HASH,
.key_size = sizeof(struct addr_desc_struct),
.value_size = sizeof(long),
.max_entries = 4096
};
SEC("collect_ips")
int xdp_ip_stats_prog(struct xdp_md *ctx) {
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct ethhdr *eth = data;
struct iphdr *iph = data + sizeof(struct ethhdr);
if (data_end >= (void *) (eth + sizeof(struct ethhdr))) {
if (eth->h_proto == htons(ETH_P_IP)) {
struct addr_desc_struct addr_desc = {.src_ip = iph->saddr};
long init_val = 1;
long *value = bpf_map_lookup_elem(&addr_map, &addr_desc);
if (value) {
__sync_fetch_and_add(value, 1);
} else {
bpf_map_update_elem(&addr_map, &addr_desc, &init_val, BPF_ANY);
}
}
}
return XDP_PASS;
}
char _license[] SEC("license") = "GPL";
设置:
macOS 主机,VirtualBox 来宾 运行 Lubuntu。我在 VM 上创建了一个 'host only adapter'。 VM 和 macOS 在 192.168.56.x 网络上都有一个接口,IP 分别为 192.168.56.102 和 192.168.56.1。此 XDP 程序使用 xdp-loader
程序在 VM 界面上成功加载。
测试 #1:
运行 IP 192.168.56.102 上虚拟机上的 HTTP 服务器。从 macOS 卷曲此 IP。
观察:XDP 程序没有捕获任何数据包。
测试#2:
运行 192.168.56.1 上 macOS 上的 HTTP 服务器。从 VM 卷曲此 IP。
观察:XDP 程序捕获了一些从 macOS 发送到 VM 的数据包。 Wireshark 指示 XDP 应该收到更多数据包。
测试#3:
从 macOS 通过 SSH 连接到 VM 上的 192.168.56.102。
观察:XDP 没有捕获数据包
在所有测试中,我应该会看到 XDP 程序处理的数据包。
根据 Andrew 的评论,这是更新后的函数。主要问题出在 if (data_end >= (void *) (eth + sizeof(struct ethhdr)))
,这会导致数据包过冲。我应该投射到 char *
。单独使用 data
不符合标准,但在 clang 中有效,因为它将字节添加到 void *
,而不是 bytes*sizeof(some pointer)
.
SEC("collect_ips")
int xdp_ip_stats_prog(struct xdp_md *ctx) {
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct ethhdr *eth = data;
struct iphdr *iph = (char *) data + sizeof(struct ethhdr);
// Without adding sizeof(struct iphdr) results in xdp-loader complaining about "invalid access to packet"
if (data_end >= (void *) ((char *) data + sizeof(struct ethhdr) + sizeof(struct iphdr))) {
if (eth->h_proto == htons(ETH_P_IP)) {
struct addr_desc_struct addr_desc = {.src_ip = iph->saddr};
long init_val = 1;
long *value = bpf_map_lookup_elem(&addr_map, &addr_desc);
if (value) {
__sync_fetch_and_add(value, 1);
} else {
bpf_map_update_elem(&addr_map, &addr_desc, &init_val, BPF_ANY);
}
}
}
return XDP_PASS;
}