xref: /freebsd/usr.bin/bluetooth/btsockstat/btsockstat.c (revision af23369a6deaaeb612ab266eb88b8bb8d560c322)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * btsockstat.c
5  *
6  * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * $Id: btsockstat.c,v 1.8 2003/05/21 22:40:25 max Exp $
31  * $FreeBSD$
32  */
33 
34 #include <sys/types.h>
35 #include <sys/callout.h>
36 #include <sys/param.h>
37 #include <sys/protosw.h>
38 #include <sys/queue.h>
39 #include <sys/socket.h>
40 #define	_WANT_SOCKET
41 #include <sys/socketvar.h>
42 
43 #include <net/if.h>
44 
45 #define L2CAP_SOCKET_CHECKED
46 #include <bluetooth.h>
47 #include <err.h>
48 #include <fcntl.h>
49 #include <kvm.h>
50 #include <limits.h>
51 #include <nlist.h>
52 
53 #include <netgraph/bluetooth/include/ng_bluetooth.h>
54 #include <netgraph/bluetooth/include/ng_btsocket_hci_raw.h>
55 #include <netgraph/bluetooth/include/ng_btsocket_l2cap.h>
56 #include <netgraph/bluetooth/include/ng_btsocket_rfcomm.h>
57 
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <unistd.h>
62 
63 static void	hcirawpr   (kvm_t *kvmd, u_long addr);
64 static void	l2caprawpr (kvm_t *kvmd, u_long addr);
65 static void	l2cappr    (kvm_t *kvmd, u_long addr);
66 static void	l2caprtpr  (kvm_t *kvmd, u_long addr);
67 static void	rfcommpr   (kvm_t *kvmd, u_long addr);
68 static void	rfcommpr_s (kvm_t *kvmd, u_long addr);
69 
70 static char *	bdaddrpr   (bdaddr_p const ba, char *str, int len);
71 
72 static kvm_t *	kopen      (char const *memf);
73 static int	kread      (kvm_t *kvmd, u_long addr, char *buffer, int size);
74 
75 static void	usage      (void);
76 
77 /*
78  * List of symbols
79  */
80 
81 static struct nlist	nl[] = {
82 #define N_HCI_RAW	0
83 	{ "_ng_btsocket_hci_raw_sockets" },
84 #define N_L2CAP_RAW	1
85 	{ "_ng_btsocket_l2cap_raw_sockets" },
86 #define N_L2CAP		2
87 	{ "_ng_btsocket_l2cap_sockets" },
88 #define N_L2CAP_RAW_RT	3
89 	{ "_ng_btsocket_l2cap_raw_rt" },
90 #define N_L2CAP_RT	4
91 	{ "_ng_btsocket_l2cap_rt" },
92 #define N_RFCOMM	5
93 	{ "_ng_btsocket_rfcomm_sockets" },
94 #define N_RFCOMM_S	6
95 	{ "_ng_btsocket_rfcomm_sessions" },
96 	{ "" },
97 };
98 
99 #define state2str(x) \
100 	(((x) >= sizeof(states)/sizeof(states[0]))? "UNKNOWN" : states[(x)])
101 
102 /*
103  * Main
104  */
105 
106 static int	numeric_bdaddr = 0;
107 
108 int
109 main(int argc, char *argv[])
110 {
111 	int	 opt, proto = -1, route = 0;
112 	kvm_t	*kvmd = NULL;
113 	char	*memf = NULL;
114 
115 	while ((opt = getopt(argc, argv, "hnM:p:r")) != -1) {
116 		switch (opt) {
117 		case 'n':
118 			numeric_bdaddr = 1;
119 			break;
120 
121 		case 'M':
122 			memf = optarg;
123 			break;
124 
125 		case 'p':
126 			if (strcasecmp(optarg, "hci_raw") == 0)
127 				proto = N_HCI_RAW;
128 			else if (strcasecmp(optarg, "l2cap_raw") == 0)
129 				proto = N_L2CAP_RAW;
130 			else if (strcasecmp(optarg, "l2cap") == 0)
131 				proto = N_L2CAP;
132 			else if (strcasecmp(optarg, "rfcomm") == 0)
133 				proto = N_RFCOMM;
134 			else if (strcasecmp(optarg, "rfcomm_s") == 0)
135 				proto = N_RFCOMM_S;
136 			else
137 				usage();
138 				/* NOT REACHED */
139 			break;
140 
141 		case 'r':
142 			route = 1;
143 			break;
144 
145 		case 'h':
146 		default:
147 			usage();
148 			/* NOT REACHED */
149 		}
150 	}
151 
152 	if ((proto == N_HCI_RAW || proto == N_RFCOMM || proto == N_RFCOMM_S) && route)
153 		usage();
154 		/* NOT REACHED */
155 
156 	/*
157 	 * Discard setgid privileges if not the running kernel so that
158 	 * bad guys can't print interesting stuff from kernel memory.
159 	 */
160 	if (memf != NULL)
161 		if (setgid(getgid()) != 0)
162 			err(1, "setgid");
163 
164 	kvmd = kopen(memf);
165 	if (kvmd == NULL)
166 		return (1);
167 
168 	switch (proto) {
169 	case N_HCI_RAW:
170 		hcirawpr(kvmd, nl[N_HCI_RAW].n_value);
171 		break;
172 
173 	case N_L2CAP_RAW:
174 		if (route)
175 			l2caprtpr(kvmd, nl[N_L2CAP_RAW_RT].n_value);
176 		else
177 			l2caprawpr(kvmd, nl[N_L2CAP_RAW].n_value);
178 		break;
179 
180 	case N_L2CAP:
181 		if (route)
182 			l2caprtpr(kvmd, nl[N_L2CAP_RT].n_value);
183 		else
184 			l2cappr(kvmd, nl[N_L2CAP].n_value);
185 		break;
186 
187 	case N_RFCOMM:
188 		rfcommpr(kvmd, nl[N_RFCOMM].n_value);
189 		break;
190 
191 	case N_RFCOMM_S:
192 		rfcommpr_s(kvmd, nl[N_RFCOMM_S].n_value);
193 		break;
194 
195 	default:
196 		if (route) {
197 			l2caprtpr(kvmd, nl[N_L2CAP_RAW_RT].n_value);
198 			l2caprtpr(kvmd, nl[N_L2CAP_RT].n_value);
199 		} else {
200 			hcirawpr(kvmd, nl[N_HCI_RAW].n_value);
201 			l2caprawpr(kvmd, nl[N_L2CAP_RAW].n_value);
202 			l2cappr(kvmd, nl[N_L2CAP].n_value);
203 			rfcommpr_s(kvmd, nl[N_RFCOMM_S].n_value);
204 			rfcommpr(kvmd, nl[N_RFCOMM].n_value);
205 		}
206 		break;
207 	}
208 
209 	return (kvm_close(kvmd));
210 } /* main */
211 
212 /*
213  * Print raw HCI sockets
214  */
215 
216 static void
217 hcirawpr(kvm_t *kvmd, u_long addr)
218 {
219 	ng_btsocket_hci_raw_pcb_p	this = NULL, next = NULL;
220 	ng_btsocket_hci_raw_pcb_t	pcb;
221 	struct socket			so;
222 	int				first = 1;
223 
224 	if (addr == 0)
225 		return;
226 
227         if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0)
228 		return;
229 
230 	for ( ; this != NULL; this = next) {
231 		if (kread(kvmd, (u_long) this, (char *) &pcb, sizeof(pcb)) < 0)
232 			return;
233 		if (kread(kvmd, (u_long) pcb.so, (char *) &so, sizeof(so)) < 0)
234 			return;
235 
236 		next = LIST_NEXT(&pcb, next);
237 
238 		if (first) {
239 			first = 0;
240 			fprintf(stdout,
241 "Active raw HCI sockets\n" \
242 "%-8.8s %-8.8s %-6.6s %-6.6s %-6.6s %-16.16s\n",
243 				"Socket",
244 				"PCB",
245 				"Flags",
246 				"Recv-Q",
247 				"Send-Q",
248 				"Local address");
249 		}
250 
251 		if (pcb.addr.hci_node[0] == 0) {
252 			pcb.addr.hci_node[0] = '*';
253 			pcb.addr.hci_node[1] = 0;
254 		}
255 
256 		fprintf(stdout,
257 "%-8lx %-8lx %-6.6x %6d %6d %-16.16s\n",
258 			(unsigned long) pcb.so,
259 			(unsigned long) this,
260 			pcb.flags,
261 			so.so_rcv.sb_ccc,
262 			so.so_snd.sb_ccc,
263 			pcb.addr.hci_node);
264 	}
265 } /* hcirawpr */
266 
267 /*
268  * Print raw L2CAP sockets
269  */
270 
271 static void
272 l2caprawpr(kvm_t *kvmd, u_long addr)
273 {
274 	ng_btsocket_l2cap_raw_pcb_p	this = NULL, next = NULL;
275 	ng_btsocket_l2cap_raw_pcb_t	pcb;
276 	struct socket			so;
277 	int				first = 1;
278 
279 	if (addr == 0)
280 		return;
281 
282         if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0)
283 		return;
284 
285 	for ( ; this != NULL; this = next) {
286 		if (kread(kvmd, (u_long) this, (char *) &pcb, sizeof(pcb)) < 0)
287 			return;
288 		if (kread(kvmd, (u_long) pcb.so, (char *) &so, sizeof(so)) < 0)
289 			return;
290 
291 		next = LIST_NEXT(&pcb, next);
292 
293 		if (first) {
294 			first = 0;
295 			fprintf(stdout,
296 "Active raw L2CAP sockets\n" \
297 "%-8.8s %-8.8s %-6.6s %-6.6s %-17.17s\n",
298 				"Socket",
299 				"PCB",
300 				"Recv-Q",
301 				"Send-Q",
302 				"Local address");
303 		}
304 
305 		fprintf(stdout,
306 "%-8lx %-8lx %6d %6d %-17.17s\n",
307 			(unsigned long) pcb.so,
308 			(unsigned long) this,
309 			so.so_rcv.sb_ccc,
310 			so.so_snd.sb_ccc,
311 			bdaddrpr(&pcb.src, NULL, 0));
312 	}
313 } /* l2caprawpr */
314 
315 /*
316  * Print L2CAP sockets
317  */
318 
319 static void
320 l2cappr(kvm_t *kvmd, u_long addr)
321 {
322 	static char const * const	states[] = {
323 	/* NG_BTSOCKET_L2CAP_CLOSED */		"CLOSED",
324 	/* NG_BTSOCKET_L2CAP_CONNECTING */	"CON",
325 	/* NG_BTSOCKET_L2CAP_CONFIGURING */	"CONFIG",
326 	/* NG_BTSOCKET_L2CAP_OPEN */		"OPEN",
327 	/* NG_BTSOCKET_L2CAP_DISCONNECTING */	"DISCON"
328 	};
329 
330 	ng_btsocket_l2cap_pcb_p	this = NULL, next = NULL;
331 	ng_btsocket_l2cap_pcb_t	pcb;
332 	struct socket		so;
333 	int			first = 1;
334 	char			local[24], remote[24];
335 
336 	if (addr == 0)
337 		return;
338 
339         if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0)
340 		return;
341 
342 	for ( ; this != NULL; this = next) {
343 		if (kread(kvmd, (u_long) this, (char *) &pcb, sizeof(pcb)) < 0)
344 			return;
345 		if (kread(kvmd, (u_long) pcb.so, (char *) &so, sizeof(so)) < 0)
346 			return;
347 
348 		next = LIST_NEXT(&pcb, next);
349 
350 		if (first) {
351 			first = 0;
352 			fprintf(stdout,
353 "Active L2CAP sockets\n" \
354 "%-8.8s %-6.6s %-6.6s %-23.23s %-17.17s %-5.5s %s\n",
355 				"PCB",
356 				"Recv-Q",
357 				"Send-Q",
358 				"Local address/PSM",
359 				"Foreign address",
360 				"CID",
361 				"State");
362 		}
363 
364 		fprintf(stdout,
365 "%-8lx %6d %6d %-17.17s/%-5d %-17.17s %-5d %s\n",
366 			(unsigned long) this,
367 			so.so_rcv.sb_ccc,
368 			so.so_snd.sb_ccc,
369 			bdaddrpr(&pcb.src, local, sizeof(local)),
370 			pcb.psm,
371 			bdaddrpr(&pcb.dst, remote, sizeof(remote)),
372 			pcb.cid,
373 			(so.so_options & SO_ACCEPTCONN)?
374 				"LISTEN" : state2str(pcb.state));
375 	}
376 } /* l2cappr */
377 
378 /*
379  * Print L2CAP routing table
380  */
381 
382 static void
383 l2caprtpr(kvm_t *kvmd, u_long addr)
384 {
385 	ng_btsocket_l2cap_rtentry_p	this = NULL, next = NULL;
386 	ng_btsocket_l2cap_rtentry_t	rt;
387 	int				first = 1;
388 
389 	if (addr == 0)
390 		return;
391 
392 	if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0)
393 		return;
394 
395 	for ( ; this != NULL; this = next) {
396 		if (kread(kvmd, (u_long) this, (char *) &rt, sizeof(rt)) < 0)
397 			return;
398 
399 		next = LIST_NEXT(&rt, next);
400 
401 		if (first) {
402 			first = 0;
403 			fprintf(stdout,
404 "Known %sL2CAP routes\n", (addr == nl[N_L2CAP_RAW_RT].n_value)?  "raw " : "");
405 			fprintf(stdout,
406 "%-8.8s %-8.8s %-17.17s\n",	"RTentry",
407 				"Hook",
408 				"BD_ADDR");
409 		}
410 
411 		fprintf(stdout,
412 "%-8lx %-8lx %-17.17s\n",
413 			(unsigned long) this,
414 			(unsigned long) rt.hook,
415 			bdaddrpr(&rt.src, NULL, 0));
416 	}
417 } /* l2caprtpr */
418 
419 /*
420  * Print RFCOMM sockets
421  */
422 
423 static void
424 rfcommpr(kvm_t *kvmd, u_long addr)
425 {
426 	static char const * const	states[] = {
427 	/* NG_BTSOCKET_RFCOMM_DLC_CLOSED */	   "CLOSED",
428 	/* NG_BTSOCKET_RFCOMM_DLC_W4_CONNECT */	   "W4CON",
429 	/* NG_BTSOCKET_RFCOMM_DLC_CONFIGURING */   "CONFIG",
430 	/* NG_BTSOCKET_RFCOMM_DLC_CONNECTING */    "CONN",
431 	/* NG_BTSOCKET_RFCOMM_DLC_CONNECTED */     "OPEN",
432 	/* NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING */ "DISCON"
433 	};
434 
435 	ng_btsocket_rfcomm_pcb_p	this = NULL, next = NULL;
436 	ng_btsocket_rfcomm_pcb_t	pcb;
437 	struct socket			so;
438 	int				first = 1;
439 	char				local[24], remote[24];
440 
441 	if (addr == 0)
442 		return;
443 
444         if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0)
445 		return;
446 
447 	for ( ; this != NULL; this = next) {
448 		if (kread(kvmd, (u_long) this, (char *) &pcb, sizeof(pcb)) < 0)
449 			return;
450 		if (kread(kvmd, (u_long) pcb.so, (char *) &so, sizeof(so)) < 0)
451 			return;
452 
453 		next = LIST_NEXT(&pcb, next);
454 
455 		if (first) {
456 			first = 0;
457 			fprintf(stdout,
458 "Active RFCOMM sockets\n" \
459 "%-8.8s %-6.6s %-6.6s %-17.17s %-17.17s %-4.4s %-4.4s %s\n",
460 				"PCB",
461 				"Recv-Q",
462 				"Send-Q",
463 				"Local address",
464 				"Foreign address",
465 				"Chan",
466 				"DLCI",
467 				"State");
468 		}
469 
470 		fprintf(stdout,
471 "%-8lx %6d %6d %-17.17s %-17.17s %-4d %-4d %s\n",
472 			(unsigned long) this,
473 			so.so_rcv.sb_ccc,
474 			so.so_snd.sb_ccc,
475 			bdaddrpr(&pcb.src, local, sizeof(local)),
476 			bdaddrpr(&pcb.dst, remote, sizeof(remote)),
477 			pcb.channel,
478 			pcb.dlci,
479 			(so.so_options & SO_ACCEPTCONN)?
480 				"LISTEN" : state2str(pcb.state));
481 	}
482 } /* rfcommpr */
483 
484 /*
485  * Print RFCOMM sessions
486  */
487 
488 static void
489 rfcommpr_s(kvm_t *kvmd, u_long addr)
490 {
491 	static char const * const	states[] = {
492 	/* NG_BTSOCKET_RFCOMM_SESSION_CLOSED */	       "CLOSED",
493 	/* NG_BTSOCKET_RFCOMM_SESSION_LISTENING */     "LISTEN",
494 	/* NG_BTSOCKET_RFCOMM_SESSION_CONNECTING */    "CONNECTING",
495 	/* NG_BTSOCKET_RFCOMM_SESSION_CONNECTED */     "CONNECTED",
496 	/* NG_BTSOCKET_RFCOMM_SESSION_OPEN */          "OPEN",
497 	/* NG_BTSOCKET_RFCOMM_SESSION_DISCONNECTING */ "DISCONNECTING"
498 	};
499 
500 	ng_btsocket_rfcomm_session_p	this = NULL, next = NULL;
501 	ng_btsocket_rfcomm_session_t	s;
502 	struct socket			so;
503 	int				first = 1;
504 
505 	if (addr == 0)
506 		return;
507 
508         if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0)
509 		return;
510 
511 	for ( ; this != NULL; this = next) {
512 		if (kread(kvmd, (u_long) this, (char *) &s, sizeof(s)) < 0)
513 			return;
514 		if (kread(kvmd, (u_long) s.l2so, (char *) &so, sizeof(so)) < 0)
515 			return;
516 
517 		next = LIST_NEXT(&s, next);
518 
519 		if (first) {
520 			first = 0;
521 			fprintf(stdout,
522 "Active RFCOMM sessions\n" \
523 "%-8.8s %-8.8s %-4.4s %-5.5s %-5.5s %-4.4s %s\n",
524 				"L2PCB",
525 				"PCB",
526 				"Flags",
527 				"MTU",
528 				"Out-Q",
529 				"DLCs",
530 				"State");
531 		}
532 
533 		fprintf(stdout,
534 "%-8lx %-8lx %-4x %-5d %-5d %-4s %s\n",
535 			(unsigned long) so.so_pcb,
536 			(unsigned long) this,
537 			s.flags,
538 			s.mtu,
539 			s.outq.len,
540 			LIST_EMPTY(&s.dlcs)? "No" : "Yes",
541 			state2str(s.state));
542 	}
543 } /* rfcommpr_s */
544 
545 /*
546  * Return BD_ADDR as string
547  */
548 
549 static char *
550 bdaddrpr(bdaddr_p const ba, char *str, int len)
551 {
552 	static char	 buffer[MAXHOSTNAMELEN];
553 	struct hostent	*he = NULL;
554 
555 	if (str == NULL) {
556 		str = buffer;
557 		len = sizeof(buffer);
558 	}
559 
560 	if (memcmp(ba, NG_HCI_BDADDR_ANY, sizeof(*ba)) == 0) {
561 		str[0] = '*';
562 		str[1] = 0;
563 
564 		return (str);
565 	}
566 
567 	if (!numeric_bdaddr &&
568 	    (he = bt_gethostbyaddr((char *)ba, sizeof(*ba), AF_BLUETOOTH)) != NULL) {
569 		strlcpy(str, he->h_name, len);
570 
571 		return (str);
572 	}
573 
574 	bt_ntoa(ba, str);
575 
576 	return (str);
577 } /* bdaddrpr */
578 
579 /*
580  * Open kvm
581  */
582 
583 static kvm_t *
584 kopen(char const *memf)
585 {
586 	kvm_t	*kvmd = NULL;
587 	char	 errbuf[_POSIX2_LINE_MAX];
588 
589 	kvmd = kvm_openfiles(NULL, memf, NULL, O_RDONLY, errbuf);
590 	if (setgid(getgid()) != 0)
591 		err(1, "setgid");
592 	if (kvmd == NULL) {
593 		warnx("kvm_openfiles: %s", errbuf);
594 		return (NULL);
595 	}
596 
597 	if (kvm_nlist(kvmd, nl) < 0) {
598 		warnx("kvm_nlist: %s", kvm_geterr(kvmd));
599 		goto fail;
600 	}
601 
602 	if (nl[0].n_type == 0) {
603 		warnx("kvm_nlist: no namelist");
604 		goto fail;
605 	}
606 
607 	return (kvmd);
608 fail:
609 	kvm_close(kvmd);
610 
611 	return (NULL);
612 } /* kopen */
613 
614 /*
615  * Read kvm
616  */
617 
618 static int
619 kread(kvm_t *kvmd, u_long addr, char *buffer, int size)
620 {
621 	if (kvmd == NULL || buffer == NULL)
622 		return (-1);
623 
624 	if (kvm_read(kvmd, addr, buffer, size) != size) {
625 		warnx("kvm_read: %s", kvm_geterr(kvmd));
626 		return (-1);
627 	}
628 
629 	return (0);
630 } /* kread */
631 
632 /*
633  * Print usage and exit
634  */
635 
636 static void
637 usage(void)
638 {
639 	fprintf(stdout, "Usage: btsockstat [-M core ] [-n] [-p proto] [-r]\n");
640 	exit(255);
641 } /* usage */
642 
643