反汇编ebpf目标文件实例sockex2_kern.c
此示例的作用是以协议中的目的ip地址统计报文的个数和累计长度;
本文按照普通的ipv4 以太网报文逻辑走查了一遍sockex2_kern汇编,按照step进行了相应的注释,并在最后简单总结了一下。
内核5.15
c代码 samples/bpf/sockex2_kern.c
#include <uapi/linux/bpf.h>
#include <uapi/linux/in.h>
#include <uapi/linux/if.h>
#include <uapi/linux/if_ether.h>
#include <uapi/linux/ip.h>
#include <uapi/linux/ipv6.h>
#include <uapi/linux/if_tunnel.h>
#include <bpf/bpf_helpers.h>
#include "bpf_legacy.h"
#define IP_MF 0x2000
#define IP_OFFSET 0x1FFF
struct vlan_hdr {
__be16 h_vlan_TCI;
__be16 h_vlan_encapsulated_proto;
};
struct flow_key_record {
__be32 src;
__be32 dst;
union {
__be32 ports;
__be16 port16[2];
};
__u16 thoff;
__u8 ip_proto;
};
static inline int proto_ports_offset(__u64 proto)
{
switch (proto) {
case IPPROTO_TCP:
case IPPROTO_UDP:
case IPPROTO_DCCP:
case IPPROTO_ESP:
case IPPROTO_SCTP:
case IPPROTO_UDPLITE:
return 0;
case IPPROTO_AH:
return 4;
default:
return 0;
}
}
static inline int ip_is_fragment(struct __sk_buff *ctx, __u64 nhoff)
{
return load_half(ctx, nhoff + offsetof(struct iphdr, frag_off))
& (IP_MF | IP_OFFSET);
}
static inline __u32 ipv6_addr_hash(struct __sk_buff *ctx, __u64 off)
{
__u64 w0 = load_word(ctx, off);
__u64 w1 = load_word(ctx, off + 4);
__u64 w2 = load_word(ctx, off + 8);
__u64 w3 = load_word(ctx, off + 12);
return (__u32)(w0 ^ w1 ^ w2 ^ w3);
}
static inline __u64 parse_ip(struct __sk_buff *skb, __u64 nhoff, __u64 *ip_proto,
struct flow_key_record *flow)
{
__u64 verlen;
if (unlikely(ip_is_fragment(skb, nhoff)))
*ip_proto = 0;
else
*ip_proto = load_byte(skb, nhoff + offsetof(struct iphdr, protocol));
if (*ip_proto != IPPROTO_GRE) {
flow->src = load_word(skb, nhoff + offsetof(struct iphdr, saddr));
flow->dst = load_word(skb, nhoff + offsetof(struct iphdr, daddr));
}
verlen = load_byte(skb, nhoff + 0/*offsetof(struct iphdr, ihl)*/);
if (likely(verlen == 0x45))
nhoff += 20;
else
nhoff += (verlen & 0xF) << 2;
return nhoff;
}
static inline __u64 parse_ipv6(struct __sk_buff *skb, __u64 nhoff, __u64 *ip_proto,
struct flow_key_record *flow)
{
*ip_proto = load_byte(skb,
nhoff + offsetof(struct ipv6hdr, nexthdr));
flow->src = ipv6_addr_hash(skb,
nhoff + offsetof(struct ipv6hdr, saddr));
flow->dst = ipv6_addr_hash(skb,
nhoff + offsetof(struct ipv6hdr, daddr));
nhoff += sizeof(struct ipv6hdr);
return nhoff;
}
static inline bool flow_dissector(struct __sk_buff *skb,
struct flow_key_record *flow)
{
__u64 nhoff = ETH_HLEN;
__u64 ip_proto;
__u64 proto = load_half(skb, 12);
int poff;
if (proto == ETH_P_8021AD) {
proto = load_half(skb, nhoff + offsetof(struct vlan_hdr,
h_vlan_encapsulated_proto));
nhoff += sizeof(struct vlan_hdr);
}
if (proto == ETH_P_8021Q) {
proto = load_half(skb, nhoff + offsetof(struct vlan_hdr,
h_vlan_encapsulated_proto));
nhoff += sizeof(struct vlan_hdr);
}
if (likely(proto == ETH_P_IP))
nhoff = parse_ip(skb, nhoff, &ip_proto, flow);
else if (proto == ETH_P_IPV6)
nhoff = parse_ipv6(skb, nhoff, &ip_proto, flow);
else
return false;
switch (ip_proto) {
case IPPROTO_GRE: {
struct gre_hdr {
__be16 flags;
__be16 proto;
};
__u64 gre_flags = load_half(skb,
nhoff + offsetof(struct gre_hdr, flags));
__u64 gre_proto = load_half(skb,
nhoff + offsetof(struct gre_hdr, proto));
if (gre_flags & (GRE_VERSION|GRE_ROUTING))
break;
proto = gre_proto;
nhoff += 4;
if (gre_flags & GRE_CSUM)
nhoff += 4;
if (gre_flags & GRE_KEY)
nhoff += 4;
if (gre_flags & GRE_SEQ)
nhoff += 4;
if (proto == ETH_P_8021Q) {
proto = load_half(skb,
nhoff + offsetof(struct vlan_hdr,
h_vlan_encapsulated_proto));
nhoff += sizeof(struct vlan_hdr);
}
if (proto == ETH_P_IP)
nhoff = parse_ip(skb, nhoff, &ip_proto, flow);
else if (proto == ETH_P_IPV6)
nhoff = parse_ipv6(skb, nhoff, &ip_proto, flow);
else
return false;
break;
}
case IPPROTO_IPIP:
nhoff = parse_ip(skb, nhoff, &ip_proto, flow);
break;
case IPPROTO_IPV6:
nhoff = parse_ipv6(skb, nhoff, &ip_proto, flow);
break;
default:
break;
}
flow->ip_proto = ip_proto;
poff = proto_ports_offset(ip_proto);
if (poff >= 0) {
nhoff += poff;
flow->ports = load_word(skb, nhoff);
}
flow->thoff = (__u16) nhoff;
return true;
}
struct pair {
long packets;
long bytes;
};
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, __be32);
__type(value, struct pair);
__uint(max_entries, 1024);
} hash_map SEC(".maps");
SEC("socket2")
int bpf_prog2(struct __sk_buff *skb)
{
struct flow_key_record flow = {};
struct pair *value;
u32 key;
if (!flow_dissector(skb, &flow))
return 0;
key = flow.dst;
value = bpf_map_lookup_elem(&hash_map, &key);
if (value) {
__sync_fetch_and_add(&value->packets, 1);
__sync_fetch_and_add(&value->bytes, skb->len);
} else {
struct pair val = {1, skb->len};
bpf_map_update_elem(&hash_map, &key, &val, BPF_ANY);
}
return 0;
}
char _license[] SEC("license") = "GPL";
反汇编
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 目标MAC地址 (Destination MAC Address) | 6 字节
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 源MAC地址 (Source MAC Address) | 6 字节
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 类型/长度 (Type/Length) | Payload | 最多 1500 字节
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| CRC校验码 (CRC) | 4 字节
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
cl@cl-Alienware-13:~/source/stable/linux/samples/bpf$ llvm-objdump -S sockex2_kern.o
sockex2_kern.o: file format ELF64-BPF
Disassembly of section socket2:
0000000000000000 bpf_prog2:
; {
0: bf 16 00 00 00 00 00 00 r6 = r1
1: b7 08 00 00 0e 00 00 00 r8 = 14
; __u64 proto = load_half(skb, 12);
2: 28 00 00 00 0c 00 00 00 r0 = *(u16 *)skb[12] 【step 1. 如上以太网格式,12取的是 类型】
; if (proto == ETH_P_8021AD) {
3: 55 00 02 00 a8 88 00 00 if r0 != 34984 goto +2 <LBB0_2> 【step 2. 判断是否是 ETH_P_8021AD(vlan in vlan), 如果是,则继续分解vlan头, 一般不是,跳转】
4: b7 08 00 00 12 00 00 00 r8 = 18
; proto = load_half(skb, nhoff + offsetof(struct vlan_hdr,
5: 28 00 00 00 10 00 00 00 r0 = *(u16 *)skb[16] 【nhoff(14) + offsetof(struct vlan_hdr, h_vlan_encapsulated_proto)(2)】
0000000000000030 LBB0_2:
; if (proto == ETH_P_8021Q) {
6: 55 00 04 00 00 81 00 00 if r0 != 33024 goto +4 <LBB0_4> 【step 3. 判断是否是 ETH_P_8021Q, 如果是,则继续分解vlan头, 一般不是,跳转】
; proto = load_half(skb, nhoff + offsetof(struct vlan_hdr,
7: bf 87 00 00 00 00 00 00 r7 = r8
8: 07 07 00 00 02 00 00 00 r7 += 2
9: 48 70 00 00 00 00 00 00 r0 = *(u16 *)skb[r7]
; nhoff += sizeof(struct vlan_hdr);
10: 07 08 00 00 04 00 00 00 r8 += 4
0000000000000058 LBB0_4:
; if (likely(proto == ETH_P_IP))
11: 15 00 4f 00 00 08 00 00 if r0 == 2048 goto +79 <LBB0_6> 【step 4. 如果是ipv4则跳转,一般是,跳转】
12: 15 00 01 00 dd 86 00 00 if r0 == 34525 goto +1 <LBB0_12>
13: 05 00 a2 00 00 00 00 00 goto +162 <LBB0_39>
0000000000000070 LBB0_12:
; nhoff + offsetof(struct ipv6hdr, nexthdr));
14: bf 87 00 00 00 00 00 00 r7 = r8
15: 07 07 00 00 06 00 00 00 r7 += 6
; *ip_proto = load_byte(skb,
16: 50 70 00 00 00 00 00 00 r0 = *(u8 *)skb[r7]
17: 7b 0a d8 ff 00 00 00 00 *(u64 *)(r10 - 40) = r0
; __u64 w3 = load_word(ctx, off + 12);
18: bf 87 00 00 00 00 00 00 r7 = r8
19: 07 07 00 00 24 00 00 00 r7 += 36
20: 40 70 00 00 00 00 00 00 r0 = *(u32 *)skb[r7]
21: 7b 0a e0 ff 00 00 00 00 *(u64 *)(r10 - 32) = r0
; __u64 w2 = load_word(ctx, off + 8);
22: bf 87 00 00 00 00 00 00 r7 = r8
23: 07 07 00 00 20 00 00 00 r7 += 32
24: 40 70 00 00 00 00 00 00 r0 = *(u32 *)skb[r7]
25: 7b 0a d0 ff 00 00 00 00 *(u64 *)(r10 - 48) = r0
; nhoff + offsetof(struct ipv6hdr, daddr));
26: bf 87 00 00 00 00 00 00 r7 = r8
27: 07 07 00 00 18 00 00 00 r7 += 24
; __u64 w0 = load_word(ctx, off);
28: 40 70 00 00 00 00 00 00 r0 = *(u32 *)skb[r7]
29: bf 09 00 00 00 00 00 00 r9 = r0
; __u64 w1 = load_word(ctx, off + 4);
30: bf 87 00 00 00 00 00 00 r7 = r8
31: 07 07 00 00 1c 00 00 00 r7 += 28
32: 40 70 00 00 00 00 00 00 r0 = *(u32 *)skb[r7]
33: 79 a1 d8 ff 00 00 00 00 r1 = *(u64 *)(r10 - 40)
; nhoff += sizeof(struct ipv6hdr);
34: 07 08 00 00 28 00 00 00 r8 += 40
; return (__u32)(w0 ^ w1 ^ w2 ^ w3);
35: af 90 00 00 00 00 00 00 r0 ^= r9
36: 79 a2 d0 ff 00 00 00 00 r2 = *(u64 *)(r10 - 48)
37: af 20 00 00 00 00 00 00 r0 ^= r2
38: 79 a2 e0 ff 00 00 00 00 r2 = *(u64 *)(r10 - 32)
39: af 20 00 00 00 00 00 00 r0 ^= r2
40: 7b 0a e0 ff 00 00 00 00 *(u64 *)(r10 - 32) = r0
41: bf 87 00 00 00 00 00 00 r7 = r8
; switch (ip_proto) {
42: 15 01 4d 00 04 00 00 00 if r1 == 4 goto +77 <LBB0_30>
0000000000000158 LBB0_13:
43: 15 01 58 00 29 00 00 00 if r1 == 41 goto +88 <LBB0_33> 【step 13. r1存储的还是 ip的 Protocol 字段, 判断是否是IPPROTO_IPV6,一般不是,继续】
44: 55 01 6a 00 2f 00 00 00 if r1 != 47 goto +106 <LBB0_36> 【step 14. 判断是否是IPPROTO_GRE, 一般不是,跳转】
; nhoff + offsetof(struct gre_hdr, proto));
45: bf 78 00 00 00 00 00 00 r8 = r7
46: 07 08 00 00 02 00 00 00 r8 += 2
; __u64 gre_proto = load_half(skb,
47: 48 80 00 00 00 00 00 00 r0 = *(u16 *)skb[r8]
48: bf 08 00 00 00 00 00 00 r8 = r0
; __u64 gre_flags = load_half(skb,
49: 48 70 00 00 00 00 00 00 r0 = *(u16 *)skb[r7]
; if (gre_flags & (GRE_VERSION|GRE_ROUTING))
50: bf 01 00 00 00 00 00 00 r1 = r0
51: 57 01 00 00 40 07 00 00 r1 &= 1856
52: 55 01 62 00 00 00 00 00 if r1 != 0 goto +98 <LBB0_36>
; if (gre_flags & GRE_CSUM)
53: bf 01 00 00 00 00 00 00 r1 = r0
54: 67 01 00 00 38 00 00 00 r1 <<= 56
55: c7 01 00 00 38 00 00 00 r1 s>>= 56
56: b7 03 00 00 08 00 00 00 r3 = 8
57: b7 02 00 00 00 00 00 00 r2 = 0
58: 6d 12 01 00 00 00 00 00 if r2 s> r1 goto +1 <LBB0_18>
59: b7 03 00 00 04 00 00 00 r3 = 4
00000000000001e0 LBB0_18:
60: 0f 73 00 00 00 00 00 00 r3 += r7
; if (gre_flags & GRE_KEY)
61: bf 01 00 00 00 00 00 00 r1 = r0
62: 57 01 00 00 20 00 00 00 r1 &= 32
63: 15 01 01 00 00 00 00 00 if r1 == 0 goto +1 <LBB0_20>
64: 07 03 00 00 04 00 00 00 r3 += 4
0000000000000208 LBB0_20:
; if (gre_flags & GRE_SEQ)
65: 57 00 00 00 10 00 00 00 r0 &= 16
66: 15 00 01 00 00 00 00 00 if r0 == 0 goto +1 <LBB0_22>
67: 07 03 00 00 04 00 00 00 r3 += 4
0000000000000220 LBB0_22:
; if (proto == ETH_P_8021Q) {
68: 55 08 07 00 00 81 00 00 if r8 != 33024 goto +7 <LBB0_24>
; nhoff + offsetof(struct vlan_hdr,
69: bf 37 00 00 00 00 00 00 r7 = r3
70: 07 07 00 00 02 00 00 00 r7 += 2
71: bf 38 00 00 00 00 00 00 r8 = r3
; proto = load_half(skb,
72: 48 70 00 00 00 00 00 00 r0 = *(u16 *)skb[r7]
73: bf 83 00 00 00 00 00 00 r3 = r8
74: bf 08 00 00 00 00 00 00 r8 = r0
; nhoff += sizeof(struct vlan_hdr);
75: 07 03 00 00 04 00 00 00 r3 += 4
0000000000000260 LBB0_24:
; if (proto == ETH_P_IP)
76: 15 08 65 00 dd 86 00 00 if r8 == 34525 goto +101 <LBB0_29>
77: 55 08 62 00 00 08 00 00 if r8 != 2048 goto +98 <LBB0_39>
; return load_half(ctx, nhoff + offsetof(struct iphdr, frag_off))
78: bf 37 00 00 00 00 00 00 r7 = r3
79: 07 07 00 00 06 00 00 00 r7 += 6
80: bf 38 00 00 00 00 00 00 r8 = r3
81: 48 70 00 00 00 00 00 00 r0 = *(u16 *)skb[r7]
; if (unlikely(ip_is_fragment(skb, nhoff)))
82: 57 00 00 00 ff 3f 00 00 r0 &= 16383
83: 55 00 04 00 00 00 00 00 if r0 != 0 goto +4 <LBB0_28>
; *ip_proto = load_byte(skb, nhoff + offsetof(struct iphdr, protocol)); 【parse_ip 内联了,相同的汇编逻辑在多出出现】
84: bf 87 00 00 00 00 00 00 r7 = r8
85: 07 07 00 00 09 00 00 00 r7 += 9
86: 50 70 00 00 00 00 00 00 r0 = *(u8 *)skb[r7]
; if (*ip_proto != IPPROTO_GRE) {
87: 15 00 3f 00 2f 00 00 00 if r0 == 47 goto +63 <LBB0_36>
00000000000002c0 LBB0_28:
; flow->dst = load_word(skb, nhoff + offsetof(struct iphdr, daddr));
88: 07 08 00 00 10 00 00 00 r8 += 16
89: 40 80 00 00 00 00 00 00 r0 = *(u32 *)skb[r8]
90: 05 00 3b 00 00 00 00 00 goto +59 <LBB0_35>
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version| IHL |Type of Service| Total Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identification |Flags| Fragment Offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Time to Live | Protocol | Header Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source IP Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination IP Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
00000000000002d8 LBB0_6: 【step 5. 是 ipv4, r8还是14,是 ip 协议开始的地方】
; return load_half(ctx, nhoff + offsetof(struct iphdr, frag_off))
91: bf 87 00 00 00 00 00 00 r7 = r8
92: 07 07 00 00 06 00 00 00 r7 += 6
93: 48 70 00 00 00 00 00 00 r0 = *(u16 *)skb[r7] 【以太网头14 + 6字节为Flags字段】
94: b7 09 00 00 00 00 00 00 r9 = 0
; if (unlikely(ip_is_fragment(skb, nhoff)))
95: 57 00 00 00 ff 3f 00 00 r0 &= 16383 【 & (IP_MF | IP_OFFSET);】
96: 55 00 07 00 00 00 00 00 if r0 != 0 goto +7 <LBB0_8> 【step 6. 如果 Flags字段包含如上标记,则*ip_proto = 0; 一般继续】
; *ip_proto = load_byte(skb, nhoff + offsetof(struct iphdr, protocol));
97: bf 87 00 00 00 00 00 00 r7 = r8
98: 07 07 00 00 09 00 00 00 r7 += 9
99: 50 70 00 00 00 00 00 00 r0 = *(u8 *)skb[r7] 【step 7. 获取ip Protocol 字段】
100: bf 09 00 00 00 00 00 00 r9 = r0
101: b7 01 00 00 2f 00 00 00 r1 = 47
102: b7 00 00 00 00 00 00 00 r0 = 0
; if (*ip_proto != IPPROTO_GRE) {
103: 15 09 04 00 2f 00 00 00 if r9 == 47 goto +4 <LBB0_9> 【step 8. 正常应该不等于 IPPROTO_GRE, 继续】
0000000000000340 LBB0_8:
; flow->dst = load_word(skb, nhoff + offsetof(struct iphdr, daddr));
104: bf 87 00 00 00 00 00 00 r7 = r8
105: 07 07 00 00 10 00 00 00 r7 += 16 【+16 取得 Destination IP Address 】
106: 40 70 00 00 00 00 00 00 r0 = *(u32 *)skb[r7]
107: bf 91 00 00 00 00 00 00 r1 = r9
0000000000000360 LBB0_9:
108: 7b 0a e0 ff 00 00 00 00 *(u64 *)(r10 - 32) = r0 【step 9. 存储 Destination IP Address 到栈里面】
109: bf 19 00 00 00 00 00 00 r9 = r1
; verlen = load_byte(skb, nhoff + 0/*offsetof(struct iphdr, ihl)*/);
110: 50 80 00 00 00 00 00 00 r0 = *(u8 *)skb[r8] 【取 Version 字段】
111: b7 07 00 00 14 00 00 00 r7 = 20
; if (likely(verlen == 0x45))
112: 15 00 03 00 45 00 00 00 if r0 == 69 goto +3 <LBB0_11> 【step 10. (likely(verlen == 0x45)), 跳转】
113: 67 00 00 00 02 00 00 00 r0 <<= 2
114: 57 00 00 00 3c 00 00 00 r0 &= 60
115: bf 07 00 00 00 00 00 00 r7 = r0
00000000000003a0 LBB0_11:
116: 0f 87 00 00 00 00 00 00 r7 += r8 【step 11. 以太网头 + ip头】
117: bf 91 00 00 00 00 00 00 r1 = r9 【r9还是 ip协议的 Protocol 字段】
; switch (ip_proto) {
118: 15 01 01 00 04 00 00 00 if r1 == 4 goto +1 <LBB0_30> 【step 12. 如果是IPIP tunnels 协议(ip in ip),则跳转继续parse_ip,正常一般不是ip in ip,继续】
119: 05 00 b3 ff 00 00 00 00 goto -77 <LBB0_13>
00000000000003c0 LBB0_30:
; return load_half(ctx, nhoff + offsetof(struct iphdr, frag_off))
120: bf 78 00 00 00 00 00 00 r8 = r7
121: 07 08 00 00 06 00 00 00 r8 += 6
122: 48 80 00 00 00 00 00 00 r0 = *(u16 *)skb[r8]
; if (unlikely(ip_is_fragment(skb, nhoff)))
123: 57 00 00 00 ff 3f 00 00 r0 &= 16383
124: 55 00 04 00 00 00 00 00 if r0 != 0 goto +4 <LBB0_32>
; *ip_proto = load_byte(skb, nhoff + offsetof(struct iphdr, protocol));
125: bf 78 00 00 00 00 00 00 r8 = r7
126: 07 08 00 00 09 00 00 00 r8 += 9
127: 50 80 00 00 00 00 00 00 r0 = *(u8 *)skb[r8]
; if (*ip_proto != IPPROTO_GRE) {
128: 15 00 16 00 2f 00 00 00 if r0 == 47 goto +22 <LBB0_36>
0000000000000408 LBB0_32:
; flow->dst = load_word(skb, nhoff + offsetof(struct iphdr, daddr));
129: 07 07 00 00 10 00 00 00 r7 += 16
130: 40 70 00 00 00 00 00 00 r0 = *(u32 *)skb[r7]
131: 05 00 12 00 00 00 00 00 goto +18 <LBB0_35>
0000000000000420 LBB0_33:
; __u64 w3 = load_word(ctx, off + 12);
132: bf 78 00 00 00 00 00 00 r8 = r7
133: 07 08 00 00 24 00 00 00 r8 += 36
134: 40 80 00 00 00 00 00 00 r0 = *(u32 *)skb[r8]
135: 7b 0a e0 ff 00 00 00 00 *(u64 *)(r10 - 32) = r0
; __u64 w2 = load_word(ctx, off + 8);
136: bf 78 00 00 00 00 00 00 r8 = r7
137: 07 08 00 00 20 00 00 00 r8 += 32
138: 40 80 00 00 00 00 00 00 r0 = *(u32 *)skb[r8]
139: bf 09 00 00 00 00 00 00 r9 = r0
; nhoff + offsetof(struct ipv6hdr, daddr));
140: bf 78 00 00 00 00 00 00 r8 = r7
141: 07 08 00 00 18 00 00 00 r8 += 24
; __u64 w0 = load_word(ctx, off);
142: 40 80 00 00 00 00 00 00 r0 = *(u32 *)skb[r8]
143: bf 08 00 00 00 00 00 00 r8 = r0
; __u64 w1 = load_word(ctx, off + 4);
144: 07 07 00 00 1c 00 00 00 r7 += 28
145: 40 70 00 00 00 00 00 00 r0 = *(u32 *)skb[r7]
; return (__u32)(w0 ^ w1 ^ w2 ^ w3);
146: af 80 00 00 00 00 00 00 r0 ^= r8
147: af 90 00 00 00 00 00 00 r0 ^= r9
00000000000004a0 LBB0_34:
148: 79 a1 e0 ff 00 00 00 00 r1 = *(u64 *)(r10 - 32)
149: af 10 00 00 00 00 00 00 r0 ^= r1
00000000000004b0 LBB0_35:
150: 7b 0a e0 ff 00 00 00 00 *(u64 *)(r10 - 32) = r0
00000000000004b8 LBB0_36:
; key = flow.dst;
151: 79 a1 e0 ff 00 00 00 00 r1 = *(u64 *)(r10 - 32) 【step 15. 这里取出前面存在栈里面的 Destination IP Address, 可以看到flow->ports没有编译出来】
152: 63 1a fc ff 00 00 00 00 *(u32 *)(r10 - 4) = r1 【step 16. 这里存入r10-4的位置,下面r2取为key】
153: bf a2 00 00 00 00 00 00 r2 = r10
154: 07 02 00 00 fc ff ff ff r2 += -4
; value = bpf_map_lookup_elem(&hash_map, &key);
155: 18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0 ll
157: 85 00 00 00 01 00 00 00 call 1 【调用bpf_map_lookup_elem】
; if (value) {
158: 15 00 05 00 00 00 00 00 if r0 == 0 goto +5 <LBB0_38> 【如果没搜索到,则bpf_map_update_elem, 此ip的第一次则跳转17-1】
159: b7 01 00 00 01 00 00 00 r1 = 1
; __sync_fetch_and_add(&value->packets, 1);
160: db 10 00 00 00 00 00 00 lock *(u64 *)(r0 + 0) += r1 【step 17. 搜索到了packets+1】
; __sync_fetch_and_add(&value->bytes, skb->len);
161: 61 61 00 00 00 00 00 00 r1 = *(u32 *)(r6 + 0) 【step 18. r6还是入参 skb, __sync_fetch_and_add(&value->bytes, skb->len);】
162: db 10 08 00 00 00 00 00 lock *(u64 *)(r0 + 8) += r1
163: 05 00 0c 00 00 00 00 00 goto +12 <LBB0_39>
0000000000000520 LBB0_38: 【step 17-1. bpf_map_update_elem 流程】
164: b7 01 00 00 01 00 00 00 r1 = 1
; struct pair val = {1, skb->len};
165: 7b 1a e8 ff 00 00 00 00 *(u64 *)(r10 - 24) = r1
166: 61 61 00 00 00 00 00 00 r1 = *(u32 *)(r6 + 0)
167: 7b 1a f0 ff 00 00 00 00 *(u64 *)(r10 - 16) = r1
168: bf a2 00 00 00 00 00 00 r2 = r10
169: 07 02 00 00 fc ff ff ff r2 += -4
170: bf a3 00 00 00 00 00 00 r3 = r10
171: 07 03 00 00 e8 ff ff ff r3 += -24
; bpf_map_update_elem(&hash_map, &key, &val, BPF_ANY);
172: 18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0 ll
174: b7 04 00 00 00 00 00 00 r4 = 0
175: 85 00 00 00 02 00 00 00 call 2 【第一次ipdate后继续走LBB0_39退出】
0000000000000580 LBB0_39:
; }
176: b7 00 00 00 00 00 00 00 r0 = 0
177: 95 00 00 00 00 00 00 00 exit 【step 19. 退出,每个 ebpf section 只有一个exit】
0000000000000590 LBB0_29:
; __u64 w3 = load_word(ctx, off + 12);
178: bf 37 00 00 00 00 00 00 r7 = r3
179: 07 07 00 00 24 00 00 00 r7 += 36
180: bf 39 00 00 00 00 00 00 r9 = r3
181: 40 70 00 00 00 00 00 00 r0 = *(u32 *)skb[r7]
182: 7b 0a e0 ff 00 00 00 00 *(u64 *)(r10 - 32) = r0
; __u64 w2 = load_word(ctx, off + 8);
183: bf 97 00 00 00 00 00 00 r7 = r9
184: 07 07 00 00 20 00 00 00 r7 += 32
185: 40 70 00 00 00 00 00 00 r0 = *(u32 *)skb[r7]
186: bf 07 00 00 00 00 00 00 r7 = r0
; nhoff + offsetof(struct ipv6hdr, daddr));
187: bf 98 00 00 00 00 00 00 r8 = r9
188: 07 08 00 00 18 00 00 00 r8 += 24
; __u64 w0 = load_word(ctx, off);
189: 40 80 00 00 00 00 00 00 r0 = *(u32 *)skb[r8]
190: bf 08 00 00 00 00 00 00 r8 = r0
; __u64 w1 = load_word(ctx, off + 4);
191: 07 09 00 00 1c 00 00 00 r9 += 28
192: 40 90 00 00 00 00 00 00 r0 = *(u32 *)skb[r9]
; return (__u32)(w0 ^ w1 ^ w2 ^ w3);
193: af 80 00 00 00 00 00 00 r0 ^= r8
194: af 70 00 00 00 00 00 00 r0 ^= r7
195: 05 00 d0 ff 00 00 00 00 goto -48 <LBB0_34>
cl@cl-Alienware-13:~/source/stable/linux/samples/bpf$
几个特点:
- r6默认作为入参skb一直都是skb,这个寄存器没有被重复使用过
- c代码里面只赋值但最后没有实际使用的代码最后没有被编译出来,
例如flow->src, flow->ports在c代码里面看到有赋值,但最终没有使用,
即没有存入map,所以也没编译出来。 - 这里内联使用较多,在汇编里面可以看到相同逻辑的汇编。如parse_ip 内联了,相同的汇编逻辑在多出出现
- 调用helper函数使用call 加一个数字,这个数字代表了一个函数,helper函数调用的入参是r1,r2, 返回值是r0
5.各个LBB0_*的含义跟汇编里面标号是一样的,如果LBB0_末端还没跳转,则继续执行下一个LBB0_ - 单个section只有一个exit出口
程序总体还是比较好懂,有一些比较有意思的东西比如ETH_P_8021AD(vlan in vlan), IPPROTO_IPIP (ip in ip), 分析汇编能立刻明白他们就是个封装。
其他:
如果不内联,函数调用汇编是什么样子的?可以实验一下