1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | #include <……> // tcpdump -dd 生成出的伪代码块 // instruction format: // opcode: 16bits; jt: 8bits; jf: 8bits; k: 32bits static struct sock_filter code[] = { { 0x28, 0, 0, 0x0000000c }, // (000) ldh [12] { 0x15, 0, 4, 0x000086dd }, // (001) jeq #0x86dd jt 2 jf 6 { 0x30, 0, 0, 0x00000014 }, // (002) ldb [20] { 0x15, 0, 11, 0x00000006 }, // (003) jeq #0x6 jt 4 jf 15 { 0x28, 0, 0, 0x00000038 }, // (004) ldh [56] { 0x15, 8, 9, 0x00000438 }, // (005) jeq #0x438 jt 14 jf 15 { 0x15, 0, 8, 0x00000800 }, // (006) jeq #0x800 jt 7 jf 15 { 0x30, 0, 0, 0x00000017 }, // (007) ldb [23] { 0x15, 0, 6, 0x00000006 }, // (008) jeq #0x6 jt 9 jf 15 { 0x28, 0, 0, 0x00000014 }, // (009) ldh [20] { 0x45, 4, 0, 0x00001fff }, // (010) jset #0x1fff jt 15 jf 11 { 0xb1, 0, 0, 0x0000000e }, // (011) ldxb 4*([14]&0xf) { 0x48, 0, 0, 0x00000010 }, // (012) ldh [x + 16] { 0x15, 0, 1, 0x00000438 }, // (013) jeq #0x438 jt 14 jf 15 { 0x6, 0, 0, 0x00040000 }, // (014) ret #262144 { 0x6, 0, 0, 0x00000000 }, // (015) ret #0 }; int main(int argc, char **argv) { // …… struct sock_fprog bpf = { sizeof(code)/sizeof(struct sock_filter), code }; // …… // 1. 创建 raw socket s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); // …… // 2. 将 socket 绑定给指定的 ethernet dev name = argv[1]; // ethernet dev 由 arg 1 传入 memset(&addr, 0, sizeof(addr)); addr.sll_ifindex = if_nametoindex(name); // …… if (bind(s, (struct sockaddr *)&addr, sizeof(addr))) { // …… } // 3. 利用 SO_ATTACH_FILTER 将 bpf 代码块传入内核 if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf))) { // …… } for (; ;) { bytes = recv(s, buf, sizeof(buf), 0); // 4. 利用 recv()获取符合条件的报文 // …… ip_header = (struct iphdr *)(buf + sizeof(struct ether_header)); inet_ntop(AF_INET, &ip_header->saddr, src_addr_str, sizeof(src_addr_str)); inet_ntop(AF_INET, &ip_header->daddr, dst_addr_str, sizeof(dst_addr_str)); printf("IPv%d proto=%d src=%s dst=%s\n", ip_header->version, ip_header->protocol, src_addr_str, dst_addr_str); } return 0; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #include <…> // 篇幅所限,清单 3 和 4 都只罗列出部分关键代码,有兴趣一窥全貌的读者可以移步 http://elixir.free-electrons.com/linux/v4.12.6/source/samples/bpf深入学习 int main(int ac, char **argv) { // 1. eBPF 的伪代码位于 sockex1_kern.o 中,这是一个由 llvm 生成的 elf 格式文件,指令集为 bpf; snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); if (load_bpf_file(filename)) { // load_bpf_file()定义于 bpf_load.c,利用 libelf 来解析 sockex1_kern.o // 并利用 bpf_load_program 将解析出的伪代码 attach 进内核; } // 2. 因为 sockex1_kern.o 中 bpf 程序的类型为 BPF_PROG_TYPE_SOCKET_FILTER // 所以这里需要用用 SO_ATTACH_BPF 来指明程序的 sk_filter 要挂载到哪一个套接字上 sock = open_raw_sock("lo"); assert(setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, prog_fd, sizeof(prog_fd[0])) == 0); //…… for (i = 0; i < 5; i++) { // 3. 利用 map 机制获取经由 lo 发出的 tcp 报文的总长度 key = IPPROTO_TCP; assert(bpf_map_lookup_elem(map_fd[0], &key, &tcp_cnt) == 0); // …… } return 0; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | #include <……> // 预先定义好的 map 对象 // 这里要注意好其实 map 是需要由用户空间程序调用 bpf_create_map()进行创建的 // 在这里定义的 map 对象,实际上会在 load_bpf_file()解析 ELF 文件的同时被解析和创建出来 // 这里的 SEC(NAME)宏表示在当前 obj 文件中新增一个段(section) struct bpf_map_def SEC("maps") my_map = { .type = BPF_MAP_TYPE_ARRAY, .key_size = sizeof(u32), .value_size = sizeof(long), .max_entries = 256, }; SEC("socket1") int bpf_prog1(struct __sk_buff *skb) { // 这个例子比较简单,仅仅是读取输入报文的包头中的协议位而已 // 这里的 load_byte 实际指向了 llvm 的 built-in 函数 asm(llvm.bpf.load.byte) // 用于生成 eBPF 指令 BPF_LD_ABS 和 BPF_LD_IND int index = load_byte(skb, ETH_HLEN + offsetof(struct iphdr, protocol)); long *value; // …… // 根据 key(&index,注意这是一个指向函数的引用)获取对应的 value value = bpf_map_lookup_elem(&my_map, &index); if (value) __sync_fetch_and_add(value, skb->len); //这里的__sync_fetch_and_add 是 llvm 中的内嵌函数,表示 atomic 加操作 return 0; } // 为了满足 GPL 毒药的需求,所有会注入内核的 BPF 代码都须显式的支持 GPL 协议 char _license[] SEC("license") = "GPL"; |
欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) | Powered by Discuz! 7.0.0 |