反汇编ebpf目标文件实例sockex2_kern.c

反汇编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$ 

几个特点:

  1. r6默认作为入参skb一直都是skb,这个寄存器没有被重复使用过
  2. c代码里面只赋值但最后没有实际使用的代码最后没有被编译出来,
    例如flow->src, flow->ports在c代码里面看到有赋值,但最终没有使用,
    即没有存入map,所以也没编译出来。
  3. 这里内联使用较多,在汇编里面可以看到相同逻辑的汇编。如parse_ip 内联了,相同的汇编逻辑在多出出现
  4. 调用helper函数使用call 加一个数字,这个数字代表了一个函数,helper函数调用的入参是r1,r2, 返回值是r0
    5.各个LBB0_*的含义跟汇编里面标号是一样的,如果LBB0_末端还没跳转,则继续执行下一个LBB0_
  5. 单个section只有一个exit出口

程序总体还是比较好懂,有一些比较有意思的东西比如ETH_P_8021AD(vlan in vlan), IPPROTO_IPIP (ip in ip), 分析汇编能立刻明白他们就是个封装。

其他:

如果不内联,函数调用汇编是什么样子的?可以实验一下

  • 9
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值