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