forked from cilium/ebpf
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tcprtt.c
116 lines (99 loc) · 3.01 KB
/
tcprtt.c
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// +build ignore
#include "common.h"
#include "bpf_endian.h"
#include "bpf_tracing.h"
#define AF_INET 2
char __license[] SEC("license") = "Dual MIT/GPL";
/**
* For CO-RE relocatable eBPF programs, __attribute__((preserve_access_index))
* preserves the offset of the specified fields in the original kernel struct.
* So here we don't need to include "vmlinux.h". Instead we only need to define
* the kernel struct and their fields the eBPF program actually requires.
*
* Also note that BTF-enabled programs like fentry, fexit, fmod_ret, tp_btf,
* lsm, etc. declared using the BPF_PROG macro can read kernel memory without
* needing to call bpf_probe_read*().
*/
/**
* struct sock_common is the minimal network layer representation of sockets.
* This is a simplified copy of the kernel's struct sock_common.
* This copy contains only the fields needed for this example to
* fetch the source and destination port numbers and IP addresses.
*/
struct sock_common {
union {
struct {
// skc_daddr is destination IP address
__be32 skc_daddr;
// skc_rcv_saddr is the source IP address
__be32 skc_rcv_saddr;
};
};
union {
struct {
// skc_dport is the destination TCP/UDP port
__be16 skc_dport;
// skc_num is the source TCP/UDP port
__u16 skc_num;
};
};
// skc_family is the network address family (2 for IPV4)
short unsigned int skc_family;
} __attribute__((preserve_access_index));
/**
* struct sock is the network layer representation of sockets.
* This is a simplified copy of the kernel's struct sock.
* This copy is needed only to access struct sock_common.
*/
struct sock {
struct sock_common __sk_common;
} __attribute__((preserve_access_index));
/**
* struct tcp_sock is the Linux representation of a TCP socket.
* This is a simplified copy of the kernel's struct tcp_sock.
* For this example we only need srtt_us to read the smoothed RTT.
*/
struct tcp_sock {
u32 srtt_us;
} __attribute__((preserve_access_index));
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 1 << 24);
} events SEC(".maps");
/**
* The sample submitted to userspace over a ring buffer.
* Emit struct event's type info into the ELF's BTF so bpf2go
* can generate a Go type from it.
*/
struct event {
u16 sport;
u16 dport;
u32 saddr;
u32 daddr;
u32 srtt;
};
struct event *unused_event __attribute__((unused));
SEC("fentry/tcp_close")
int BPF_PROG(tcp_close, struct sock *sk) {
if (sk->__sk_common.skc_family != AF_INET) {
return 0;
}
// The input struct sock is actually a tcp_sock, so we can type-cast
struct tcp_sock *ts = bpf_skc_to_tcp_sock(sk);
if (!ts) {
return 0;
}
struct event *tcp_info;
tcp_info = bpf_ringbuf_reserve(&events, sizeof(struct event), 0);
if (!tcp_info) {
return 0;
}
tcp_info->saddr = sk->__sk_common.skc_rcv_saddr;
tcp_info->daddr = sk->__sk_common.skc_daddr;
tcp_info->dport = bpf_ntohs(sk->__sk_common.skc_dport);
tcp_info->sport = sk->__sk_common.skc_num;
tcp_info->srtt = ts->srtt_us >> 3;
tcp_info->srtt /= 1000;
bpf_ringbuf_submit(tcp_info, 0);
return 0;
}