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

tc eBPF程序获取PID遇编译警告与函数不可用问题

解决tc/eBPF程序中PID统计的编译与加载错误

编译警告 shift count >= width of type 解决

原因

bpf_get_current_pid_tgid()返回64位__u64类型值(高32位为PID,低32位为TGID),你先将其强制转换为32位int后再右移32位,移位操作超出了32位类型的宽度,触发编译器警告。同时结构体定义中pid字段末尾的逗号属于语法错误。

修复方法

先对64位返回值执行移位操作,再转换为32位无符号整数存储PID,同时修正结构体语法:

// 修正结构体定义
struct packetdets {
    __u32 source;
    __u16 source_port;
    __u32 dest;
    __u16 dest_port;
    __u8 ip_protocol;
    __u32 pid; // 替换末尾逗号为分号
};

// 正确获取PID的代码
__u32 pid = (bpf_get_current_pid_tgid() >> 32) & 0xFFFFFFFF;

加载错误 unknown func bpf_get_current_pid_tgid#14 解决

原因

TC类型的eBPF程序默认未被授权调用进程相关的helper函数bpf_get_current_pid_tgid(),同时需要确保内核版本(≥4.15)和ebpf工具链支持该函数。

修复方法

  1. 内核版本检查:确认系统内核版本≥4.15(该函数在4.15版本内核中正式引入)。
  2. Go加载代码配置:使用cilium/ebpf库加载程序时,需要为TC程序添加对该helper函数的许可:
import "github.com/cilium/ebpf"
import "log"

// 加载程序时添加配置
progSpec, err := loadTcMain()
if err != nil {
    log.Fatalf("加载程序失败: %v", err)
}

// 方法1:添加到允许列表
progSpec.Programs["tc_main"].AllowlistHelpers = append(
    progSpec.Programs["tc_main"].AllowlistHelpers,
    ebpf.FuncBPFGetCurrentPidTgid,
)

// 方法2:允许特权helper函数(更简便)
progSpec.Programs["tc_main"].Flags |= ebpf.FlagAllowPrivilegedHelpers

// 后续加载程序的代码...
  1. 工具链更新:确保使用的bpftool和clang版本足够新,支持该helper函数的编译。

注意事项

  • 仅本地进程发起或接收的数据包能获取到有效PID,转发的数据包无法关联本地进程,PID会返回0或无效值。
  • 若内核版本低于4.15,可考虑使用bpf_get_current_task()结合内核结构体提取PID,但这种方式兼容性差且需要额外权限。

修改后的完整bpf.c代码

#include <linux/bpf.h>
#include <linux/pkt_cls.h>
#include <linux/if_ether.h>
#include "include/ip.h"
#include <linux/tcp.h>
#include <linux/udp.h>
#include "include/helpers.h"
#include <linux/sched.h>
#include <linux/socket.h>
#include <linux/net.h>

#include <linux/ip.h>
#include <linux/in.h>

#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>

#define ETH_P_IP 0x0800 /* Internet Protocol Packet */
#define PROTO_TCP 6
#define PROTO_UDP 17

struct packetdets {
    __u32 source;
    __u16 source_port;
    __u32 dest;
    __u16 dest_port;
    __u8 ip_protocol;
    __u32 pid;
};

struct {
    __uint(type, BPF_MAP_TYPE_HASH);
    __type(key, struct packetdets);
    __type(value, __u64);
    __uint(max_entries, 1000);
} pkt_count SEC(".maps");

SEC("tc_prog")
int tc_main(struct __sk_buff *skb)
{
    void *data_end = (void *)(__u64)skb->data_end;
    void *data = (void *)(__u64)skb->data;
    struct ethhdr *eth;
    struct iphdr *ip;
    struct tcphdr *tcp;
    struct udphdr *udp;

    eth = data;
    if (data + sizeof(struct ethhdr) > data_end)
        return TC_ACT_OK;

    ip = data + sizeof(struct ethhdr);
    if ((void *)ip + sizeof(struct iphdr) > data_end)
        return TC_ACT_OK;

    __u32 source = bpf_ntohl(ip->addrs.saddr);
    __u32 dest = bpf_ntohl(ip->addrs.daddr);
    if (dest == 3116855193){
        return TC_ACT_SHOT;
    }

    __u16 source_port;
    __u16 dest_port;
    __u8 ip_proto;
    if (ip->protocol == PROTO_TCP) {
        tcp = (void *)ip + ip->ihl*4;
        if ((void *)tcp + sizeof(struct tcphdr) > data_end) {
            return TC_ACT_OK;
        }
        source_port = tcp->source;
        dest_port = tcp->dest;
        ip_proto = PROTO_TCP;
    } else if (ip->protocol == PROTO_UDP) {
        udp = (void *)ip + ip->ihl*4;
        if ((void *)udp + sizeof(struct udphdr) > data_end) {
            return TC_ACT_OK;
        }
        source_port = udp->source;
        dest_port = udp->dest;
        ip_proto = PROTO_UDP;
    } else {
        return TC_ACT_OK;
    }

    // 正确获取PID
    __u32 pid = (bpf_get_current_pid_tgid() >> 32) & 0xFFFFFFFF;

    struct packetdets key = {
        .source = source,
        .source_port = source_port,
        .dest = dest,
        .dest_port = dest_port,
        .ip_protocol = ip_proto,
        .pid = pid,    
    };
    __u64 *count = bpf_map_lookup_elem(&pkt_count, &key);
    if (count) {
        __sync_fetch_and_add(count, 1);
    } else {
        __u64 value = 1;
        bpf_map_update_elem(&pkt_count, &key, &value, BPF_ANY);
    }

    char hello_str[] = "hello pkt ipv4: %u";
    bpf_trace_printk(hello_str, sizeof(hello_str), &skb->remote_ip4);
    return TC_ACT_OK;
}

char __license[] SEC("license") = "Dual MIT/GPL";

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 20:22:02