btsockstat.c revision 107120
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.2 2002/09/16 19:40:14 max Exp $
29 * $FreeBSD: head/usr.bin/bluetooth/btsockstat/btsockstat.c 107120 2002-11-20 23:01:59Z 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_hci.h>
49#include <ng_l2cap.h>
50#include <ng_btsocket.h>
51#include <ng_btsocket_hci_raw.h>
52#include <ng_btsocket_l2cap.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);
63
64static kvm_t *	kopen      (char const *memf);
65static int	kread      (kvm_t *kvmd, u_long addr, char *buffer, int size);
66
67static void	usage      (void);
68
69/*
70 * List of symbols
71 */
72
73static struct nlist	nl[] = {
74#define N_HCI_RAW	0
75	{ "_ng_btsocket_hci_raw_sockets" },
76#define N_L2CAP_RAW	1
77	{ "_ng_btsocket_l2cap_raw_sockets" },
78#define N_L2CAP		2
79	{ "_ng_btsocket_l2cap_sockets" },
80#define N_L2CAP_RAW_RT	3
81	{ "_ng_btsocket_l2cap_raw_rt" },
82#define N_L2CAP_RT	4
83	{ "_ng_btsocket_l2cap_rt" },
84	{ "" },
85};
86
87/*
88 * Main
89 */
90
91int
92main(int argc, char *argv[])
93{
94	int	 opt, proto = -1, route = 0;
95	kvm_t	*kvmd = NULL;
96	char	*memf = NULL;
97
98	while ((opt = getopt(argc, argv, "hM:p:r")) != -1) {
99		switch (opt) {
100		case 'M':
101			memf = optarg;
102			break;
103
104		case 'p':
105			if (strcasecmp(optarg, "hci_raw") == 0)
106				proto = N_HCI_RAW;
107			else if (strcasecmp(optarg, "l2cap_raw") == 0)
108				proto = N_L2CAP_RAW;
109			else if (strcasecmp(optarg, "l2cap") == 0)
110				proto = N_L2CAP;
111			else
112				usage();
113				/* NOT REACHED */
114			break;
115
116		case 'r':
117			route = 1;
118			break;
119
120		case 'h':
121		default:
122			usage();
123			/* NOT REACHED */
124		}
125	}
126
127	if (proto == N_HCI_RAW && route)
128		usage();
129		/* NOT REACHED */
130
131	kvmd = kopen(memf);
132	if (kvmd == NULL)
133		return (1);
134
135	switch (proto) {
136	case N_HCI_RAW:
137		hcirawpr(kvmd, nl[N_HCI_RAW].n_value);
138		break;
139
140	case N_L2CAP_RAW:
141		if (route)
142			l2caprtpr(kvmd, nl[N_L2CAP_RAW_RT].n_value);
143		else
144			l2caprawpr(kvmd, nl[N_L2CAP_RAW].n_value);
145		break;
146
147	case N_L2CAP:
148		if (route)
149			l2caprtpr(kvmd, nl[N_L2CAP_RT].n_value);
150		else
151			l2cappr(kvmd, nl[N_L2CAP].n_value);
152		break;
153
154	default:
155		if (route) {
156			l2caprtpr(kvmd, nl[N_L2CAP_RAW_RT].n_value);
157			l2caprtpr(kvmd, nl[N_L2CAP_RT].n_value);
158		} else {
159			hcirawpr(kvmd, nl[N_HCI_RAW].n_value);
160			l2caprawpr(kvmd, nl[N_L2CAP_RAW].n_value);
161			l2cappr(kvmd, nl[N_L2CAP].n_value);
162		}
163		break;
164	}
165
166	return (kvm_close(kvmd));
167} /* main */
168
169/*
170 * Print raw HCI sockets
171 */
172
173static void
174hcirawpr(kvm_t *kvmd, u_long addr)
175{
176	ng_btsocket_hci_raw_pcb_p	this = NULL, next = NULL;
177	ng_btsocket_hci_raw_pcb_t	pcb;
178	struct socket			so;
179	int				first = 1;
180
181	if (addr == 0)
182		return;
183
184        if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0)
185		return;
186
187	for ( ; this != NULL; this = next) {
188		if (kread(kvmd, (u_long) this, (char *) &pcb, sizeof(pcb)) < 0)
189			return;
190		if (kread(kvmd, (u_long) pcb.so, (char *) &so, sizeof(so)) < 0)
191			return;
192
193		next = LIST_NEXT(&pcb, next);
194
195		if (first) {
196			first = 0;
197			fprintf(stdout,
198"Active raw HCI sockets\n" \
199"%-8.8s %-8.8s %-6.6s %-6.6s %-6.6s %-16.16s\n",
200				"Socket",
201				"PCB",
202				"Flags",
203				"Recv-Q",
204				"Send-Q",
205				"Local address");
206		}
207
208		if (pcb.addr.hci_node[0] == 0) {
209			pcb.addr.hci_node[0] = '*';
210			pcb.addr.hci_node[1] = 0;
211		}
212
213		fprintf(stdout,
214"%-8.8x %-8.8x %-6.6x %6d %6d %-16.16s\n",
215			(int) pcb.so,
216			(int) this,
217			pcb.flags,
218			so.so_rcv.sb_cc,
219			so.so_snd.sb_cc,
220			pcb.addr.hci_node);
221	}
222} /* hcirawpr */
223
224/*
225 * Print raw L2CAP sockets
226 */
227
228static void
229l2caprawpr(kvm_t *kvmd, u_long addr)
230{
231	ng_btsocket_l2cap_raw_pcb_p	this = NULL, next = NULL;
232	ng_btsocket_l2cap_raw_pcb_t	pcb;
233	struct socket			so;
234	int				first = 1;
235	char				bdaddr[32];
236
237	if (addr == 0)
238		return;
239
240        if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0)
241		return;
242
243	for ( ; this != NULL; this = next) {
244		if (kread(kvmd, (u_long) this, (char *) &pcb, sizeof(pcb)) < 0)
245			return;
246		if (kread(kvmd, (u_long) pcb.so, (char *) &so, sizeof(so)) < 0)
247			return;
248
249		next = LIST_NEXT(&pcb, next);
250
251		if (first) {
252			first = 0;
253			fprintf(stdout,
254"Active raw L2CAP sockets\n" \
255"%-8.8s %-8.8s %-6.6s %-6.6s %-18.18s\n",
256				"Socket",
257				"PCB",
258				"Recv-Q",
259				"Send-Q",
260				"Local address");
261		}
262
263		if (memcmp(&pcb.src, NG_HCI_BDADDR_ANY, sizeof(pcb.src)) == 0) {
264			bdaddr[0] = '*';
265			bdaddr[1] = 0;
266		} else
267			snprintf(bdaddr, sizeof(bdaddr),
268"%02x:%02x:%02x:%02x:%02x:%02x",
269				pcb.src.b[5], pcb.src.b[4], pcb.src.b[3],
270				pcb.src.b[2], pcb.src.b[1], pcb.src.b[0]);
271
272		fprintf(stdout,
273"%-8.8x %-8.8x %6d %6d %-18.18s\n",
274			(int) pcb.so,
275			(int) this,
276			so.so_rcv.sb_cc,
277			so.so_snd.sb_cc,
278			bdaddr);
279	}
280} /* l2caprawpr */
281
282/*
283 * Print L2CAP sockets
284 */
285
286static void
287l2cappr(kvm_t *kvmd, u_long addr)
288{
289	static char const * const	states[] = {
290	/* NG_BTSOCKET_L2CAP_CLOSED */		"CLOSED",
291	/* NG_BTSOCKET_L2CAP_CONNECTING */	"CON",
292	/* NG_BTSOCKET_L2CAP_CONFIGURING */	"CONFIG",
293	/* NG_BTSOCKET_L2CAP_OPEN */		"OPEN",
294	/* NG_BTSOCKET_L2CAP_DISCONNECTING */	"DISCON"
295	};
296#define state2str(x) \
297	(((x) >= sizeof(states)/sizeof(states[0]))? "UNKNOWN" : states[(x)])
298
299	ng_btsocket_l2cap_pcb_p	this = NULL, next = NULL;
300	ng_btsocket_l2cap_pcb_t	pcb;
301	struct socket		so;
302	int			first = 1;
303	char			local[32], remote[32];
304
305
306	if (addr == 0)
307		return;
308
309        if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0)
310		return;
311
312	for ( ; this != NULL; this = next) {
313		if (kread(kvmd, (u_long) this, (char *) &pcb, sizeof(pcb)) < 0)
314			return;
315		if (kread(kvmd, (u_long) pcb.so, (char *) &so, sizeof(so)) < 0)
316			return;
317
318		next = LIST_NEXT(&pcb, next);
319
320		if (first) {
321			first = 0;
322			fprintf(stdout,
323"Active L2CAP sockets\n" \
324"%-8.8s %-6.6s %-6.6s %-24.24s %-18.18s %-5.5s %s\n",
325				"PCB",
326				"Recv-Q",
327				"Send-Q",
328				"Local address/PSM",
329				"Foreign address",
330				"CID",
331				"State");
332		}
333
334		if (memcmp(&pcb.src, NG_HCI_BDADDR_ANY, sizeof(pcb.src)) == 0)
335			snprintf(local, sizeof(local), "*/%d", pcb.psm);
336		else
337			snprintf(local, sizeof(local),
338"%02x:%02x:%02x:%02x:%02x:%02x/%d",
339				pcb.src.b[5], pcb.src.b[4], pcb.src.b[3],
340				pcb.src.b[2], pcb.src.b[1], pcb.src.b[0],
341				pcb.psm);
342
343		if (memcmp(&pcb.dst, NG_HCI_BDADDR_ANY, sizeof(pcb.dst)) == 0) {
344			remote[0] = '*';
345			remote[1] = 0;
346		} else
347			snprintf(remote, sizeof(remote),
348"%02x:%02x:%02x:%02x:%02x:%02x",
349				pcb.dst.b[5], pcb.dst.b[4], pcb.dst.b[3],
350				pcb.dst.b[2], pcb.dst.b[1], pcb.dst.b[0]);
351
352		fprintf(stdout,
353"%-8.8x %6d %6d %-24.24s %-18.18s %-5d %s\n",
354			(int) this,
355			so.so_rcv.sb_cc,
356			so.so_snd.sb_cc,
357			local,
358			remote,
359			pcb.cid,
360			(so.so_options & SO_ACCEPTCONN)?
361				"LISTEN" : state2str(pcb.state));
362	}
363} /* l2cappr */
364
365/*
366 * Print L2CAP routing table
367 */
368
369static void
370l2caprtpr(kvm_t *kvmd, u_long addr)
371{
372	ng_btsocket_l2cap_rtentry_p	this = NULL, next = NULL;
373	ng_btsocket_l2cap_rtentry_t	rt;
374	int				first = 1;
375	char				bdaddr[32];
376
377	if (addr == 0)
378		return;
379
380	if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0)
381		return;
382
383	for ( ; this != NULL; this = next) {
384		if (kread(kvmd, (u_long) this, (char *) &rt, sizeof(rt)) < 0)
385			return;
386
387		next = LIST_NEXT(&rt, next);
388
389		if (first) {
390			first = 0;
391			fprintf(stdout,
392"Known %sL2CAP routes\n", (addr == nl[N_L2CAP_RAW_RT].n_value)?  "raw " : "");
393			fprintf(stdout,
394"%-8.8s %-8.8s %-18.18s\n",	"RTentry",
395				"Hook",
396				"BD_ADDR");
397		}
398
399		if (memcmp(&rt.src, NG_HCI_BDADDR_ANY, sizeof(rt.src)) == 0) {
400			bdaddr[0] = '-';
401			bdaddr[1] = 0;
402		} else
403			snprintf(bdaddr, sizeof(bdaddr),
404"%02x:%02x:%02x:%02x:%02x:%02x", rt.src.b[5], rt.src.b[4], rt.src.b[3],
405				 rt.src.b[2], rt.src.b[1], rt.src.b[0]);
406
407		fprintf(stdout,
408"%-8.8x %-8.8x %-18.18s\n",
409			(int) this,
410			(int) rt.hook,
411			bdaddr);
412	}
413} /* l2caprtpr */
414
415/*
416 * Open kvm
417 */
418
419static kvm_t *
420kopen(char const *memf)
421{
422	kvm_t	*kvmd = NULL;
423	char	 errbuf[_POSIX2_LINE_MAX];
424
425	/*
426	 * Discard setgid privileges if not the running kernel so that
427	 * bad guys can't print interesting stuff from kernel memory.
428	 */
429
430	if (memf != NULL)
431		setgid(getgid());
432
433	kvmd = kvm_openfiles(NULL, memf, NULL, O_RDONLY, errbuf);
434	if (kvmd == NULL) {
435		warnx("kvm_openfiles: %s", errbuf);
436		return (NULL);
437	}
438
439	if (kvm_nlist(kvmd, nl) < 0) {
440		warnx("kvm_nlist: %s", kvm_geterr(kvmd));
441		goto fail;
442	}
443
444	if (nl[0].n_type == 0) {
445		warnx("kvm_nlist: no namelist");
446		goto fail;
447	}
448
449	return (kvmd);
450fail:
451	kvm_close(kvmd);
452
453	return (NULL);
454} /* kopen */
455
456/*
457 * Read kvm
458 */
459
460static int
461kread(kvm_t *kvmd, u_long addr, char *buffer, int size)
462{
463	if (kvmd == NULL || buffer == NULL)
464		return (-1);
465
466	if (kvm_read(kvmd, addr, buffer, size) != size) {
467		warnx("kvm_read: %s", kvm_geterr(kvmd));
468		return (-1);
469	}
470
471	return (0);
472} /* kread */
473
474/*
475 * Print usage and exit
476 */
477
478static void
479usage(void)
480{
481	fprintf(stdout, "Usage: btsockstat [-M core ] [-p proto] [-r]\n");
482	exit(255);
483} /* usage */
484
485