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