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