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