You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用eBPF XDP程序过滤数据包后遇网络拥塞及Ping不通问题

解决XDP eBPF程序挂载后无法Ping及网络拥塞问题

问题重现

我在VM中部署了以下eBPF程序,意图仅允许ICMP数据包通过并丢弃TCP数据包:

#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/icmp.h>
#include <netinet/in.h>
#include <linux/udp.h>
#include <linux/tcp.h>
#include <linux/pkt_cls.h>
#include <bpf/bpf_helpers.h>
#include <linux/bpf_perf_event.h>

struct {
    __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
    __uint(max_entries, 1);
    __type(key, int);
    __type(value, uint64_t);
} timestamp_map SEC(".maps");

struct {
    __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
    __uint(max_entries, 1);
    __type(key, int);
    __type(value, uint64_t);
} counter_map SEC(".maps");

SEC("xdp")
int icmp_timestamp(struct xdp_md *xdp) {
    // Load the Ethernet protocol
    void *data_end = (void *)(long)xdp->data_end;
    void *data = (void *)(long)xdp->data;
    struct ethhdr *eth = data;

    if (data + sizeof(struct ethhdr) > data_end)
        return XDP_DROP;

    // Process only IP packets
    if (eth->h_proto == htons(ETH_P_IP)) {

        if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) > data_end)
            return XDP_DROP;

        // Load the IP protocol
        struct iphdr *ip = data + sizeof(struct ethhdr);

        if (ip->protocol == IPPROTO_ICMP) {
            // Load the timestamp
            uint64_t timestamp = bpf_ktime_get_ns();

            // Increment the packet counter
            int key_counter = 0;
            uint64_t *counter = bpf_map_lookup_elem(&counter_map, &key_counter);
            if (counter) {
                (*counter)++;
            }

            // Store the timestamp in the per-CPU map
            int key_timestamp = 0;
            bpf_map_update_elem(&timestamp_map, &key_timestamp, &timestamp, BPF_ANY);

            // Allow the packet to pass
            return XDP_PASS;
        }
    }

    // Drop the packet for non-ICMP or non-IP packets
    return XDP_DROP;
}

char _license[] SEC("license") = "GPL";

程序编译加载成功后,执行命令sudo bpftool net attach xdp id 40 dev enp0s3将其挂载到XDP钩子及网卡enp0s3,但挂载后出现网络拥塞问题,且无法通过Ping传输数据。

问题分析

  • IP头部边界检查错误:代码中用固定的sizeof(struct iphdr)(20字节)判断IP头部长度,但IP头部实际长度由ip->ihl字段决定(单位为4字节)。若IP包带有头部选项(部分Ping包或虚拟机环境下的IP包可能包含),实际长度会超过20字节,此时边界检查会触发XDP_DROP,导致合法ICMP包被丢弃。
  • XDP挂载模式兼容性:部分虚拟机网卡驱动不支持XDP的drv(驱动级)模式,默认挂载时使用该模式会引发数据包处理异常。

解决方案

1. 修复IP头部边界检查逻辑

修改代码中的IP头部检查逻辑,用ip->ihl * 4计算实际头部长度,避免误判合法数据包:

SEC("xdp")
int icmp_timestamp(struct xdp_md *xdp) {
    void *data_end = (void *)(long)xdp->data_end;
    void *data = (void *)(long)xdp->data;
    struct ethhdr *eth = data;

    if (data + sizeof(struct ethhdr) > data_end)
        return XDP_DROP;

    if (eth->h_proto == htons(ETH_P_IP)) {
        struct iphdr *ip = data + sizeof(struct ethhdr);
        // 先检查ip指针是否在数据范围内
        if ((void *)ip + sizeof(*ip) > data_end)
            return XDP_DROP;
        // 计算实际IP头部长度并校验边界
        uint32_t ip_hdr_len = ip->ihl * 4;
        if ((void *)ip + ip_hdr_len > data_end)
            return XDP_DROP;

        if (ip->protocol == IPPROTO_ICMP) {
            uint64_t timestamp = bpf_ktime_get_ns();

            int key_counter = 0;
            uint64_t *counter = bpf_map_lookup_elem(&counter_map, &key_counter);
            if (counter) {
                (*counter)++;
            }

            int key_timestamp = 0;
            bpf_map_update_elem(&timestamp_map, &key_timestamp, &timestamp, BPF_ANY);

            return XDP_PASS;
        }
    }

    return XDP_DROP;
}

2. 切换XDP挂载模式

若虚拟机网卡不支持驱动级XDP,改用兼容性更好的skb(套接字缓冲区)模式挂载:

# 先卸载原有挂载
sudo bpftool net detach xdp dev enp0s3
# 用skb模式重新挂载
sudo bpftool net attach xdp skb id 40 dev enp0s3

3. 调试验证

  • 检查计数器增长情况,确认ICMP包被正确识别:
    # 获取counter_map的ID并查看内容
    sudo bpftool map dump id $(sudo bpftool map list | grep counter_map | awk '{print $1}')
    
  • 查看XDP挂载状态:
    sudo bpftool net list xdp dev enp0s3
    

内容的提问来源于stack exchange,提问作者Manideep G

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 15:45:22