• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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