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