btsockstat.c revision 121064
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 121064 2003-10-13 05:30:20Z bde $
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
59static void	hcirawpr   (kvm_t *kvmd, u_long addr);
60static void	l2caprawpr (kvm_t *kvmd, u_long addr);
61static void	l2cappr    (kvm_t *kvmd, u_long addr);
62static void	l2caprtpr  (kvm_t *kvmd, u_long addr);
63static void	rfcommpr   (kvm_t *kvmd, u_long addr);
64static void	rfcommpr_s (kvm_t *kvmd, u_long addr);
65
66static char *	bdaddrpr   (bdaddr_p const ba, char *str, int len);
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
102static int	numeric_bdaddr = 0;
103
104int
105main(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
212static void
213hcirawpr(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"%-8.8x %-8.8x %-6.6x %6d %6d %-16.16s\n",
254			(int) pcb.so,
255			(int) 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
267static void
268l2caprawpr(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"%-8.8x %-8.8x %6d %6d %-17.17s\n",
303			(int) pcb.so,
304			(int) 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
315static void
316l2cappr(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"%-8.8x %6d %6d %-17.17s/%-5d %-17.17s %-5d %s\n",
362			(int) 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
378static void
379l2caprtpr(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"%-8.8x %-8.8x %-17.17s\n",
409			(int) this,
410			(int) rt.hook,
411			bdaddrpr(&rt.src, NULL, 0));
412	}
413} /* l2caprtpr */
414
415/*
416 * Print RFCOMM sockets
417 */
418
419static void
420rfcommpr(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"%-8.8x %6d %6d %-17.17s %-17.17s %-4d %-4d %s\n",
468			(int) 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
484static void
485rfcommpr_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"%-8.8x %-8.8x %-4x %-5d %-5d %-4s %s\n",
531			(int) so.so_pcb,
532			(int) 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
545static char *
546bdaddrpr(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
579static kvm_t *
580kopen(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);
610fail:
611	kvm_close(kvmd);
612
613	return (NULL);
614} /* kopen */
615
616/*
617 * Read kvm
618 */
619
620static int
621kread(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
638static void
639usage(void)
640{
641	fprintf(stdout, "Usage: btsockstat [-M core ] [-n] [-p proto] [-r]\n");
642	exit(255);
643} /* usage */
644
645