记录开发ebpf 遇到的问题

1、内核态和用户态传递信息的结构定义,数据结构要对齐,数据结构成员的大小要确定;
这里定义了一个perf map,用户态用golang实现。
C的对其方式很多,按照类型长度从大大小排序,不足的地方增加pad即可。

struct event_t {
    u64 saddr[2];
    u64 daddr[2];
    u64 ipt_delay;
    u64 kernel_ip;
    //time
    u64 start_ns;
    u64 test;
    void *skb;


    u32  netns;
    u32 len;

    u32 hook;
    u32 verdict;

    int kernel_stack_id;     // call stack
    u16 tot_len;
    u16 icmpid;

    u16 icmpseq;
    u16 sport;
    u16 dport;
    u16 tcpflags;

    u8 flags;
    u8 cpu;
    u8 dir;
    u8 ip_version;
    u8 l4_proto;
    u8 icmptype;
    u8 pf;
    u8 pkt_type; //skb->pkt_type
    u8 dest_mac[6];
    char func_name[FUNCNAME_MAX_LEN];
    char ifname[IFNAMSIZ];
    char tablename[XT_TABLE_MAXNAMELEN];
};
BPF_PERF_OUTPUT(route_event);

go代码:

type traceEvent struct {
    SAddr    [2]uint64
    DAddr    [2]uint64
    IptDelay uint64
    KernelIp uint64
    StartNs  uint64
    Test     uint64
    Skb      uint64

    NetNs         uint32
    Len           uint32
    Hook          uint32
    Verdict       uint32
    KernalStackId uint32
    TotolLen      uint16
    IcmpId        uint16
    IcmpSeq       uint16
    SPort         uint16
    DPort         uint16
    TcpFlags      uint16
    Flags         uint8
    Cpu           uint8
    Dir           uint8
    IpVersion     uint8
    L4Proto       uint8
    IcmpType      uint8
    Pf            uint8
    PktType       uint8 //skb->pkt_type
    Dmac          [6]uint8

    // call stack
    FuncName  [FUNCNAME_MAX_LEN]byte
    IfName    [IFNAME_MAX_LEN]byte
    TableName [XT_TABLE_MAXNAMELEN]byte
    Pad       [10]byte
}

其中有个细节,上面C的定义中有个 int 类型的“int kernel_stack_id”,如果在go中也定义int类型会报错,需要使用uint32代替,按道理大小应该一样的。

failed to decode received data: binary.Read: invalid type *main.traceEvent

2、kprobe跟踪内核函数的时候,基本上有符号表的都能跟踪,但内核是模块化的,可能由于未加载模块导致kprobe加载失败。
如,在加载ebt_do_table的时候,报错:

Failed to load kprobe__ebt_do_table: Module: unable to find kprobe__ebt_do_table

这个函数不是内连函数,也没有static 修饰是可以跟踪的,但看不到符号表:

[root@172-25-116-187 ~]# cat /proc/kallsyms | grep do_table
ffffffffc03c9030 r __ksymtab_ipt_do_table   [ip_tables]
ffffffffc03c93c8 r __kstrtab_ipt_do_table   [ip_tables]
ffffffffc03c6020 T ipt_do_table [ip_tables]

加载一下ebtables 模块就ok了。

[root@172-25-116-166 perf]# lsmod | grep ebtable
ebtable_filter         16384  1
ebtables               36864  1 ebtable_filter
[root@172-25-116-166 perf]# cat /proc/kallsyms | grep do_table
ffffffffc0407030 r __ksymtab_ebt_do_table   [ebtables]
ffffffffc0407f22 r __kstrtab_ebt_do_table   [ebtables]
ffffffffc04038f0 T ebt_do_table [ebtables]
ffffffffc02d5030 r __ksymtab_ipt_do_table   [ip_tables]
ffffffffc02d53c8 r __kstrtab_ipt_do_table   [ip_tables]
ffffffffc02d2020 T ipt_do_table [ip_tables]

3、[]byte 转 string
C语言使用char数组存储字符串,go在使用的时候要转一下。

    event.funcName = C.GoString((*C.char)(unsafe.Pointer(&event.FuncName)))
    event.tableName = C.GoString((*C.char)(unsafe.Pointer(&event.TableName)))
    event.ifName = C.GoString((*C.char)(unsafe.Pointer(&event.IfName)))

使用string强转会有问题,举个例子,如下,在C语言中为空的情况,转成go string之后,len为64,无法使用go的字符串为空判断。

    var IfName [64]byte
    // 输出: ifName:[][64]
    fmt.Printf("ifName:[%s][%d]\n", string(IfName[:]), len(string(IfName[:])))
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。