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