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[:])))