xref: /freebsd/usr.bin/bluetooth/btsockstat/btsockstat.c (revision 4b2eaea43fec8e8792be611dea204071a10b655a)
1 /*
2  * btsockstat.c
3  *
4  * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $Id: btsockstat.c,v 1.2 2002/09/16 19:40:14 max Exp $
29  * $FreeBSD$
30  */
31 
32 #include <sys/types.h>
33 #include <sys/callout.h>
34 #include <sys/param.h>
35 #include <sys/queue.h>
36 #include <sys/socket.h>
37 #include <sys/socketvar.h>
38 
39 #include <net/if.h>
40 #include <net/if_var.h>
41 
42 #include <bitstring.h>
43 #include <err.h>
44 #include <fcntl.h>
45 #include <kvm.h>
46 #include <limits.h>
47 
48 #include <ng_hci.h>
49 #include <ng_l2cap.h>
50 #include <ng_btsocket.h>
51 #include <ng_btsocket_hci_raw.h>
52 #include <ng_btsocket_l2cap.h>
53 
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <unistd.h>
58 
59 static void	hcirawpr   (kvm_t *kvmd, u_long addr);
60 static void	l2caprawpr (kvm_t *kvmd, u_long addr);
61 static void	l2cappr    (kvm_t *kvmd, u_long addr);
62 static void	l2caprtpr  (kvm_t *kvmd, u_long addr);
63 
64 static kvm_t *	kopen      (char const *memf);
65 static int	kread      (kvm_t *kvmd, u_long addr, char *buffer, int size);
66 
67 static void	usage      (void);
68 
69 /*
70  * List of symbols
71  */
72 
73 static struct nlist	nl[] = {
74 #define N_HCI_RAW	0
75 	{ "_ng_btsocket_hci_raw_sockets" },
76 #define N_L2CAP_RAW	1
77 	{ "_ng_btsocket_l2cap_raw_sockets" },
78 #define N_L2CAP		2
79 	{ "_ng_btsocket_l2cap_sockets" },
80 #define N_L2CAP_RAW_RT	3
81 	{ "_ng_btsocket_l2cap_raw_rt" },
82 #define N_L2CAP_RT	4
83 	{ "_ng_btsocket_l2cap_rt" },
84 	{ "" },
85 };
86 
87 /*
88  * Main
89  */
90 
91 int
92 main(int argc, char *argv[])
93 {
94 	int	 opt, proto = -1, route = 0;
95 	kvm_t	*kvmd = NULL;
96 	char	*memf = NULL;
97 
98 	while ((opt = getopt(argc, argv, "hM:p:r")) != -1) {
99 		switch (opt) {
100 		case 'M':
101 			memf = optarg;
102 			break;
103 
104 		case 'p':
105 			if (strcasecmp(optarg, "hci_raw") == 0)
106 				proto = N_HCI_RAW;
107 			else if (strcasecmp(optarg, "l2cap_raw") == 0)
108 				proto = N_L2CAP_RAW;
109 			else if (strcasecmp(optarg, "l2cap") == 0)
110 				proto = N_L2CAP;
111 			else
112 				usage();
113 				/* NOT REACHED */
114 			break;
115 
116 		case 'r':
117 			route = 1;
118 			break;
119 
120 		case 'h':
121 		default:
122 			usage();
123 			/* NOT REACHED */
124 		}
125 	}
126 
127 	if (proto == N_HCI_RAW && route)
128 		usage();
129 		/* NOT REACHED */
130 
131 	kvmd = kopen(memf);
132 	if (kvmd == NULL)
133 		return (1);
134 
135 	switch (proto) {
136 	case N_HCI_RAW:
137 		hcirawpr(kvmd, nl[N_HCI_RAW].n_value);
138 		break;
139 
140 	case N_L2CAP_RAW:
141 		if (route)
142 			l2caprtpr(kvmd, nl[N_L2CAP_RAW_RT].n_value);
143 		else
144 			l2caprawpr(kvmd, nl[N_L2CAP_RAW].n_value);
145 		break;
146 
147 	case N_L2CAP:
148 		if (route)
149 			l2caprtpr(kvmd, nl[N_L2CAP_RT].n_value);
150 		else
151 			l2cappr(kvmd, nl[N_L2CAP].n_value);
152 		break;
153 
154 	default:
155 		if (route) {
156 			l2caprtpr(kvmd, nl[N_L2CAP_RAW_RT].n_value);
157 			l2caprtpr(kvmd, nl[N_L2CAP_RT].n_value);
158 		} else {
159 			hcirawpr(kvmd, nl[N_HCI_RAW].n_value);
160 			l2caprawpr(kvmd, nl[N_L2CAP_RAW].n_value);
161 			l2cappr(kvmd, nl[N_L2CAP].n_value);
162 		}
163 		break;
164 	}
165 
166 	return (kvm_close(kvmd));
167 } /* main */
168 
169 /*
170  * Print raw HCI sockets
171  */
172 
173 static void
174 hcirawpr(kvm_t *kvmd, u_long addr)
175 {
176 	ng_btsocket_hci_raw_pcb_p	this = NULL, next = NULL;
177 	ng_btsocket_hci_raw_pcb_t	pcb;
178 	struct socket			so;
179 	int				first = 1;
180 
181 	if (addr == 0)
182 		return;
183 
184         if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0)
185 		return;
186 
187 	for ( ; this != NULL; this = next) {
188 		if (kread(kvmd, (u_long) this, (char *) &pcb, sizeof(pcb)) < 0)
189 			return;
190 		if (kread(kvmd, (u_long) pcb.so, (char *) &so, sizeof(so)) < 0)
191 			return;
192 
193 		next = LIST_NEXT(&pcb, next);
194 
195 		if (first) {
196 			first = 0;
197 			fprintf(stdout,
198 "Active raw HCI sockets\n" \
199 "%-8.8s %-8.8s %-6.6s %-6.6s %-6.6s %-16.16s\n",
200 				"Socket",
201 				"PCB",
202 				"Flags",
203 				"Recv-Q",
204 				"Send-Q",
205 				"Local address");
206 		}
207 
208 		if (pcb.addr.hci_node[0] == 0) {
209 			pcb.addr.hci_node[0] = '*';
210 			pcb.addr.hci_node[1] = 0;
211 		}
212 
213 		fprintf(stdout,
214 "%-8.8x %-8.8x %-6.6x %6d %6d %-16.16s\n",
215 			(int) pcb.so,
216 			(int) this,
217 			pcb.flags,
218 			so.so_rcv.sb_cc,
219 			so.so_snd.sb_cc,
220 			pcb.addr.hci_node);
221 	}
222 } /* hcirawpr */
223 
224 /*
225  * Print raw L2CAP sockets
226  */
227 
228 static void
229 l2caprawpr(kvm_t *kvmd, u_long addr)
230 {
231 	ng_btsocket_l2cap_raw_pcb_p	this = NULL, next = NULL;
232 	ng_btsocket_l2cap_raw_pcb_t	pcb;
233 	struct socket			so;
234 	int				first = 1;
235 	char				bdaddr[32];
236 
237 	if (addr == 0)
238 		return;
239 
240         if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0)
241 		return;
242 
243 	for ( ; this != NULL; this = next) {
244 		if (kread(kvmd, (u_long) this, (char *) &pcb, sizeof(pcb)) < 0)
245 			return;
246 		if (kread(kvmd, (u_long) pcb.so, (char *) &so, sizeof(so)) < 0)
247 			return;
248 
249 		next = LIST_NEXT(&pcb, next);
250 
251 		if (first) {
252 			first = 0;
253 			fprintf(stdout,
254 "Active raw L2CAP sockets\n" \
255 "%-8.8s %-8.8s %-6.6s %-6.6s %-18.18s\n",
256 				"Socket",
257 				"PCB",
258 				"Recv-Q",
259 				"Send-Q",
260 				"Local address");
261 		}
262 
263 		if (memcmp(&pcb.src, NG_HCI_BDADDR_ANY, sizeof(pcb.src)) == 0) {
264 			bdaddr[0] = '*';
265 			bdaddr[1] = 0;
266 		} else
267 			snprintf(bdaddr, sizeof(bdaddr),
268 "%02x:%02x:%02x:%02x:%02x:%02x",
269 				pcb.src.b[5], pcb.src.b[4], pcb.src.b[3],
270 				pcb.src.b[2], pcb.src.b[1], pcb.src.b[0]);
271 
272 		fprintf(stdout,
273 "%-8.8x %-8.8x %6d %6d %-18.18s\n",
274 			(int) pcb.so,
275 			(int) this,
276 			so.so_rcv.sb_cc,
277 			so.so_snd.sb_cc,
278 			bdaddr);
279 	}
280 } /* l2caprawpr */
281 
282 /*
283  * Print L2CAP sockets
284  */
285 
286 static void
287 l2cappr(kvm_t *kvmd, u_long addr)
288 {
289 	static char const * const	states[] = {
290 	/* NG_BTSOCKET_L2CAP_CLOSED */		"CLOSED",
291 	/* NG_BTSOCKET_L2CAP_CONNECTING */	"CON",
292 	/* NG_BTSOCKET_L2CAP_CONFIGURING */	"CONFIG",
293 	/* NG_BTSOCKET_L2CAP_OPEN */		"OPEN",
294 	/* NG_BTSOCKET_L2CAP_DISCONNECTING */	"DISCON"
295 	};
296 #define state2str(x) \
297 	(((x) >= sizeof(states)/sizeof(states[0]))? "UNKNOWN" : states[(x)])
298 
299 	ng_btsocket_l2cap_pcb_p	this = NULL, next = NULL;
300 	ng_btsocket_l2cap_pcb_t	pcb;
301 	struct socket		so;
302 	int			first = 1;
303 	char			local[32], remote[32];
304 
305 
306 	if (addr == 0)
307 		return;
308 
309         if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0)
310 		return;
311 
312 	for ( ; this != NULL; this = next) {
313 		if (kread(kvmd, (u_long) this, (char *) &pcb, sizeof(pcb)) < 0)
314 			return;
315 		if (kread(kvmd, (u_long) pcb.so, (char *) &so, sizeof(so)) < 0)
316 			return;
317 
318 		next = LIST_NEXT(&pcb, next);
319 
320 		if (first) {
321 			first = 0;
322 			fprintf(stdout,
323 "Active L2CAP sockets\n" \
324 "%-8.8s %-6.6s %-6.6s %-24.24s %-18.18s %-5.5s %s\n",
325 				"PCB",
326 				"Recv-Q",
327 				"Send-Q",
328 				"Local address/PSM",
329 				"Foreign address",
330 				"CID",
331 				"State");
332 		}
333 
334 		if (memcmp(&pcb.src, NG_HCI_BDADDR_ANY, sizeof(pcb.src)) == 0)
335 			snprintf(local, sizeof(local), "*/%d", pcb.psm);
336 		else
337 			snprintf(local, sizeof(local),
338 "%02x:%02x:%02x:%02x:%02x:%02x/%d",
339 				pcb.src.b[5], pcb.src.b[4], pcb.src.b[3],
340 				pcb.src.b[2], pcb.src.b[1], pcb.src.b[0],
341 				pcb.psm);
342 
343 		if (memcmp(&pcb.dst, NG_HCI_BDADDR_ANY, sizeof(pcb.dst)) == 0) {
344 			remote[0] = '*';
345 			remote[1] = 0;
346 		} else
347 			snprintf(remote, sizeof(remote),
348 "%02x:%02x:%02x:%02x:%02x:%02x",
349 				pcb.dst.b[5], pcb.dst.b[4], pcb.dst.b[3],
350 				pcb.dst.b[2], pcb.dst.b[1], pcb.dst.b[0]);
351 
352 		fprintf(stdout,
353 "%-8.8x %6d %6d %-24.24s %-18.18s %-5d %s\n",
354 			(int) this,
355 			so.so_rcv.sb_cc,
356 			so.so_snd.sb_cc,
357 			local,
358 			remote,
359 			pcb.cid,
360 			(so.so_options & SO_ACCEPTCONN)?
361 				"LISTEN" : state2str(pcb.state));
362 	}
363 } /* l2cappr */
364 
365 /*
366  * Print L2CAP routing table
367  */
368 
369 static void
370 l2caprtpr(kvm_t *kvmd, u_long addr)
371 {
372 	ng_btsocket_l2cap_rtentry_p	this = NULL, next = NULL;
373 	ng_btsocket_l2cap_rtentry_t	rt;
374 	int				first = 1;
375 	char				bdaddr[32];
376 
377 	if (addr == 0)
378 		return;
379 
380 	if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0)
381 		return;
382 
383 	for ( ; this != NULL; this = next) {
384 		if (kread(kvmd, (u_long) this, (char *) &rt, sizeof(rt)) < 0)
385 			return;
386 
387 		next = LIST_NEXT(&rt, next);
388 
389 		if (first) {
390 			first = 0;
391 			fprintf(stdout,
392 "Known %sL2CAP routes\n", (addr == nl[N_L2CAP_RAW_RT].n_value)?  "raw " : "");
393 			fprintf(stdout,
394 "%-8.8s %-8.8s %-18.18s\n",	"RTentry",
395 				"Hook",
396 				"BD_ADDR");
397 		}
398 
399 		if (memcmp(&rt.src, NG_HCI_BDADDR_ANY, sizeof(rt.src)) == 0) {
400 			bdaddr[0] = '-';
401 			bdaddr[1] = 0;
402 		} else
403 			snprintf(bdaddr, sizeof(bdaddr),
404 "%02x:%02x:%02x:%02x:%02x:%02x", rt.src.b[5], rt.src.b[4], rt.src.b[3],
405 				 rt.src.b[2], rt.src.b[1], rt.src.b[0]);
406 
407 		fprintf(stdout,
408 "%-8.8x %-8.8x %-18.18s\n",
409 			(int) this,
410 			(int) rt.hook,
411 			bdaddr);
412 	}
413 } /* l2caprtpr */
414 
415 /*
416  * Open kvm
417  */
418 
419 static kvm_t *
420 kopen(char const *memf)
421 {
422 	kvm_t	*kvmd = NULL;
423 	char	 errbuf[_POSIX2_LINE_MAX];
424 
425 	/*
426 	 * Discard setgid privileges if not the running kernel so that
427 	 * bad guys can't print interesting stuff from kernel memory.
428 	 */
429 
430 	if (memf != NULL)
431 		setgid(getgid());
432 
433 	kvmd = kvm_openfiles(NULL, memf, NULL, O_RDONLY, errbuf);
434 	if (kvmd == NULL) {
435 		warnx("kvm_openfiles: %s", errbuf);
436 		return (NULL);
437 	}
438 
439 	if (kvm_nlist(kvmd, nl) < 0) {
440 		warnx("kvm_nlist: %s", kvm_geterr(kvmd));
441 		goto fail;
442 	}
443 
444 	if (nl[0].n_type == 0) {
445 		warnx("kvm_nlist: no namelist");
446 		goto fail;
447 	}
448 
449 	return (kvmd);
450 fail:
451 	kvm_close(kvmd);
452 
453 	return (NULL);
454 } /* kopen */
455 
456 /*
457  * Read kvm
458  */
459 
460 static int
461 kread(kvm_t *kvmd, u_long addr, char *buffer, int size)
462 {
463 	if (kvmd == NULL || buffer == NULL)
464 		return (-1);
465 
466 	if (kvm_read(kvmd, addr, buffer, size) != size) {
467 		warnx("kvm_read: %s", kvm_geterr(kvmd));
468 		return (-1);
469 	}
470 
471 	return (0);
472 } /* kread */
473 
474 /*
475  * Print usage and exit
476  */
477 
478 static void
479 usage(void)
480 {
481 	fprintf(stdout, "Usage: btsockstat [-M core ] [-p proto] [-r]\n");
482 	exit(255);
483 } /* usage */
484 
485