1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2 // Copyright (c) 2021 Yaqi Chen
3 //
4 // Based on tcpsynbl(8) from BCC by Brendan Gregg.
5 // 19-Dec-2021 Yaqi Chen Created this.
6 #include <argp.h>
7 #include <stdio.h>
8 #include <signal.h>
9 #include <unistd.h>
10 #include <time.h>
11 #include <bpf/libbpf.h>
12 #include <bpf/bpf.h>
13 #include "tcpsynbl.h"
14 #include "tcpsynbl.skel.h"
15 #include "btf_helpers.h"
16 #include "trace_helpers.h"
17
18 static struct env {
19 bool ipv4;
20 bool ipv6;
21 time_t interval;
22 int times;
23 bool timestamp;
24 bool verbose;
25 } env = {
26 .interval = 99999999,
27 .times = 99999999,
28 };
29
30 static volatile sig_atomic_t exiting = 0;
31
32 const char *argp_program_version = "tcpsynbl 0.1";
33 const char *argp_program_bug_address =
34 "https://github.com/iovisor/bcc/tree/master/libbpf-tools";
35 const char argp_program_doc[] =
36 "Summarize TCP SYN backlog as a histogram.\n"
37 "\n"
38 "USAGE: tcpsynbl [--help] [-T] [-4] [-6] [interval] [count]\n"
39 "\n"
40 "EXAMPLES:\n"
41 " tcpsynbl # summarize TCP SYN backlog as a histogram\n"
42 " tcpsynbl 1 10 # print 1 second summaries, 10 times\n"
43 " tcpsynbl -T 1 # 1s summaries with timestamps\n"
44 " tcpsynbl -4 # trace IPv4 family only\n"
45 " tcpsynbl -6 # trace IPv6 family only\n";
46
47
48 static const struct argp_option opts[] = {
49 { "timestamp", 'T', NULL, 0, "Include timestamp on output" },
50 { "ipv4", '4', NULL, 0, "Trace IPv4 family only" },
51 { "ipv6", '6', NULL, 0, "Trace IPv6 family only" },
52 { "verbose", 'v', NULL, 0, "Verbose debug output" },
53 { NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" },
54 {},
55 };
56
57
parse_arg(int key,char * arg,struct argp_state * state)58 static error_t parse_arg(int key, char *arg, struct argp_state *state)
59 {
60 static int pos_args;
61
62 switch (key) {
63 case 'h':
64 argp_state_help(state, stderr, ARGP_HELP_STD_HELP);
65 break;
66 case 'v':
67 env.verbose = true;
68 break;
69 case 'T':
70 env.timestamp = true;
71 break;
72 case '4':
73 env.ipv4 = true;
74 break;
75 case '6':
76 env.ipv6 = true;
77 break;
78 case ARGP_KEY_ARG:
79 errno = 0;
80 if (pos_args == 0) {
81 env.interval = strtol(arg, NULL, 10);
82 if (errno) {
83 fprintf(stderr, "invalid internal\n");
84 argp_usage(state);
85 }
86 } else if (pos_args == 1) {
87 env.times = strtol(arg, NULL, 10);
88 if (errno) {
89 fprintf(stderr, "invalid times\n");
90 argp_usage(state);
91 }
92 } else {
93 fprintf(stderr,
94 "unrecognized positional argument: %s\n", arg);
95 argp_usage(state);
96 }
97 pos_args++;
98 break;
99 default:
100 return ARGP_ERR_UNKNOWN;
101 }
102 return 0;
103 }
104
libbpf_print_fn(enum libbpf_print_level level,const char * format,va_list args)105 static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
106 {
107 if (level == LIBBPF_DEBUG && !env.verbose)
108 return 0;
109 return vfprintf(stderr, format, args);
110 }
111
sig_handler(int sig)112 static void sig_handler(int sig)
113 {
114 exiting = true;
115 }
116
disable_all_progs(struct tcpsynbl_bpf * obj)117 static void disable_all_progs(struct tcpsynbl_bpf *obj)
118 {
119 bpf_program__set_autoload(obj->progs.tcp_v4_syn_recv_kprobe, false);
120 bpf_program__set_autoload(obj->progs.tcp_v6_syn_recv_kprobe, false);
121 bpf_program__set_autoload(obj->progs.tcp_v4_syn_recv, false);
122 bpf_program__set_autoload(obj->progs.tcp_v6_syn_recv, false);
123 }
124
set_autoload_prog(struct tcpsynbl_bpf * obj,int version)125 static void set_autoload_prog(struct tcpsynbl_bpf *obj, int version)
126 {
127 if (version == 4) {
128 if (fentry_can_attach("tcp_v4_syn_recv_sock", NULL))
129 bpf_program__set_autoload(obj->progs.tcp_v4_syn_recv, true);
130 else
131 bpf_program__set_autoload(obj->progs.tcp_v4_syn_recv_kprobe, true);
132 }
133
134 if (version == 6){
135 if (fentry_can_attach("tcp_v6_syn_recv_sock", NULL))
136 bpf_program__set_autoload(obj->progs.tcp_v6_syn_recv, true);
137 else
138 bpf_program__set_autoload(obj->progs.tcp_v6_syn_recv_kprobe, true);
139 }
140 }
141
print_log2_hists(int fd)142 static int print_log2_hists(int fd)
143 {
144 __u64 lookup_key = -1, next_key;
145 struct hist hist;
146 int err;
147
148 while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) {
149 err = bpf_map_lookup_elem(fd, &next_key, &hist);
150 if (err < 0) {
151 fprintf(stderr, "failed to lookup hist: %d\n", err);
152 return -1;
153 }
154 printf("backlog_max = %lld\n", next_key);
155 print_log2_hist(hist.slots, MAX_SLOTS, "backlog");
156 lookup_key = next_key;
157 }
158
159 lookup_key = -1;
160 while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) {
161 err = bpf_map_delete_elem(fd, &next_key);
162 if (err < 0) {
163 fprintf(stderr, "failed to cleanup hist : %d\n", err);
164 return -1;
165 }
166 lookup_key = next_key;
167 }
168
169 return 0;
170 }
171
main(int argc,char ** argv)172 int main(int argc, char **argv)
173 {
174 LIBBPF_OPTS(bpf_object_open_opts, open_opts);
175 static const struct argp argp = {
176 .options = opts,
177 .parser = parse_arg,
178 .doc = argp_program_doc
179 };
180
181 struct tcpsynbl_bpf *obj;
182 struct tm *tm;
183 char ts[32];
184 time_t t;
185 int err, map_fd;
186
187 err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
188 if (err)
189 return err;
190
191 libbpf_set_print(libbpf_print_fn);
192
193 err = ensure_core_btf(&open_opts);
194 if (err) {
195 fprintf(stderr, "failed to fetch necessary BTF for CO-RE: %s\n", strerror(-err));
196 return 1;
197 }
198
199 obj = tcpsynbl_bpf__open_opts(&open_opts);
200 if (!obj) {
201 fprintf(stderr, "failed to open BPF object\n");
202 return 1;
203 }
204
205 disable_all_progs(obj);
206
207 if (env.ipv4) {
208 set_autoload_prog(obj, 4);
209 } else if (env.ipv6) {
210 set_autoload_prog(obj, 6);
211 } else {
212 set_autoload_prog(obj, 4);
213 set_autoload_prog(obj, 6);
214 }
215
216 err = tcpsynbl_bpf__load(obj);
217 if (err) {
218 fprintf(stderr, "failed to load BPF object: %d\n", err);
219 goto cleanup;
220 }
221
222 err = tcpsynbl_bpf__attach(obj);
223 if (err) {
224 fprintf(stderr, "failed to attach BPF programs\n");
225 goto cleanup;
226 }
227
228 map_fd= bpf_map__fd(obj->maps.hists);
229
230 signal(SIGINT, sig_handler);
231
232 printf("Tracing SYN backlog size. Ctrl-C to end.\n");
233
234 /* main: poll */
235 while (1) {
236 sleep(env.interval);
237 printf("\n");
238
239 if (env.timestamp) {
240 time(&t);
241 tm = localtime(&t);
242 strftime(ts, sizeof(ts), "%H:%M:%S", tm);
243 printf("%-8s\n", ts);
244 }
245
246 err = print_log2_hists(map_fd);
247 if (err)
248 break;
249
250 if (exiting || --env.times == 0)
251 break;
252 }
253
254 cleanup:
255 tcpsynbl_bpf__destroy(obj);
256 cleanup_core_btf(&open_opts);
257
258 return err != 0;
259 }
260