1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * btsockstat.c
5 *
6 * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 * $Id: btsockstat.c,v 1.8 2003/05/21 22:40:25 max Exp $
31 * $FreeBSD$
32 */
33
34#include <sys/types.h>
35#include <sys/callout.h>
36#include <sys/param.h>
37#include <sys/protosw.h>
38#include <sys/queue.h>
39#include <sys/socket.h>
40#define	_WANT_SOCKET
41#include <sys/socketvar.h>
42
43#include <net/if.h>
44
45#define L2CAP_SOCKET_CHECKED
46#include <bluetooth.h>
47#include <err.h>
48#include <fcntl.h>
49#include <kvm.h>
50#include <limits.h>
51#include <nlist.h>
52
53#include <netgraph/bluetooth/include/ng_bluetooth.h>
54#include <netgraph/bluetooth/include/ng_btsocket_hci_raw.h>
55#include <netgraph/bluetooth/include/ng_btsocket_l2cap.h>
56#include <netgraph/bluetooth/include/ng_btsocket_rfcomm.h>
57
58#include <stdio.h>
59#include <stdlib.h>
60#include <string.h>
61#include <unistd.h>
62
63static void	hcirawpr   (kvm_t *kvmd, u_long addr);
64static void	l2caprawpr (kvm_t *kvmd, u_long addr);
65static void	l2cappr    (kvm_t *kvmd, u_long addr);
66static void	l2caprtpr  (kvm_t *kvmd, u_long addr);
67static void	rfcommpr   (kvm_t *kvmd, u_long addr);
68static void	rfcommpr_s (kvm_t *kvmd, u_long addr);
69
70static char *	bdaddrpr   (bdaddr_p const ba, char *str, int len);
71
72static kvm_t *	kopen      (char const *memf);
73static int	kread      (kvm_t *kvmd, u_long addr, char *buffer, int size);
74
75static void	usage      (void);
76
77/*
78 * List of symbols
79 */
80
81static struct nlist	nl[] = {
82#define N_HCI_RAW	0
83	{ "_ng_btsocket_hci_raw_sockets" },
84#define N_L2CAP_RAW	1
85	{ "_ng_btsocket_l2cap_raw_sockets" },
86#define N_L2CAP		2
87	{ "_ng_btsocket_l2cap_sockets" },
88#define N_L2CAP_RAW_RT	3
89	{ "_ng_btsocket_l2cap_raw_rt" },
90#define N_L2CAP_RT	4
91	{ "_ng_btsocket_l2cap_rt" },
92#define N_RFCOMM	5
93	{ "_ng_btsocket_rfcomm_sockets" },
94#define N_RFCOMM_S	6
95	{ "_ng_btsocket_rfcomm_sessions" },
96	{ "" },
97};
98
99#define state2str(x) \
100	(((x) >= sizeof(states)/sizeof(states[0]))? "UNKNOWN" : states[(x)])
101
102/*
103 * Main
104 */
105
106static int	numeric_bdaddr = 0;
107
108int
109main(int argc, char *argv[])
110{
111	int	 opt, proto = -1, route = 0;
112	kvm_t	*kvmd = NULL;
113	char	*memf = NULL;
114
115	while ((opt = getopt(argc, argv, "hnM:p:r")) != -1) {
116		switch (opt) {
117		case 'n':
118			numeric_bdaddr = 1;
119			break;
120
121		case 'M':
122			memf = optarg;
123			break;
124
125		case 'p':
126			if (strcasecmp(optarg, "hci_raw") == 0)
127				proto = N_HCI_RAW;
128			else if (strcasecmp(optarg, "l2cap_raw") == 0)
129				proto = N_L2CAP_RAW;
130			else if (strcasecmp(optarg, "l2cap") == 0)
131				proto = N_L2CAP;
132			else if (strcasecmp(optarg, "rfcomm") == 0)
133				proto = N_RFCOMM;
134			else if (strcasecmp(optarg, "rfcomm_s") == 0)
135				proto = N_RFCOMM_S;
136			else
137				usage();
138				/* NOT REACHED */
139			break;
140
141		case 'r':
142			route = 1;
143			break;
144
145		case 'h':
146		default:
147			usage();
148			/* NOT REACHED */
149		}
150	}
151
152	if ((proto == N_HCI_RAW || proto == N_RFCOMM || proto == N_RFCOMM_S) && route)
153		usage();
154		/* NOT REACHED */
155
156	/*
157	 * Discard setgid privileges if not the running kernel so that
158	 * bad guys can't print interesting stuff from kernel memory.
159	 */
160	if (memf != NULL)
161		if (setgid(getgid()) != 0)
162			err(1, "setgid");
163
164	kvmd = kopen(memf);
165	if (kvmd == NULL)
166		return (1);
167
168	switch (proto) {
169	case N_HCI_RAW:
170		hcirawpr(kvmd, nl[N_HCI_RAW].n_value);
171		break;
172
173	case N_L2CAP_RAW:
174		if (route)
175			l2caprtpr(kvmd, nl[N_L2CAP_RAW_RT].n_value);
176		else
177			l2caprawpr(kvmd, nl[N_L2CAP_RAW].n_value);
178		break;
179
180	case N_L2CAP:
181		if (route)
182			l2caprtpr(kvmd, nl[N_L2CAP_RT].n_value);
183		else
184			l2cappr(kvmd, nl[N_L2CAP].n_value);
185		break;
186
187	case N_RFCOMM:
188		rfcommpr(kvmd, nl[N_RFCOMM].n_value);
189		break;
190
191	case N_RFCOMM_S:
192		rfcommpr_s(kvmd, nl[N_RFCOMM_S].n_value);
193		break;
194
195	default:
196		if (route) {
197			l2caprtpr(kvmd, nl[N_L2CAP_RAW_RT].n_value);
198			l2caprtpr(kvmd, nl[N_L2CAP_RT].n_value);
199		} else {
200			hcirawpr(kvmd, nl[N_HCI_RAW].n_value);
201			l2caprawpr(kvmd, nl[N_L2CAP_RAW].n_value);
202			l2cappr(kvmd, nl[N_L2CAP].n_value);
203			rfcommpr_s(kvmd, nl[N_RFCOMM_S].n_value);
204			rfcommpr(kvmd, nl[N_RFCOMM].n_value);
205		}
206		break;
207	}
208
209	return (kvm_close(kvmd));
210} /* main */
211
212/*
213 * Print raw HCI sockets
214 */
215
216static void
217hcirawpr(kvm_t *kvmd, u_long addr)
218{
219	ng_btsocket_hci_raw_pcb_p	this = NULL, next = NULL;
220	ng_btsocket_hci_raw_pcb_t	pcb;
221	struct socket			so;
222	int				first = 1;
223
224	if (addr == 0)
225		return;
226
227        if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0)
228		return;
229
230	for ( ; this != NULL; this = next) {
231		if (kread(kvmd, (u_long) this, (char *) &pcb, sizeof(pcb)) < 0)
232			return;
233		if (kread(kvmd, (u_long) pcb.so, (char *) &so, sizeof(so)) < 0)
234			return;
235
236		next = LIST_NEXT(&pcb, next);
237
238		if (first) {
239			first = 0;
240			fprintf(stdout,
241"Active raw HCI sockets\n" \
242"%-8.8s %-8.8s %-6.6s %-6.6s %-6.6s %-16.16s\n",
243				"Socket",
244				"PCB",
245				"Flags",
246				"Recv-Q",
247				"Send-Q",
248				"Local address");
249		}
250
251		if (pcb.addr.hci_node[0] == 0) {
252			pcb.addr.hci_node[0] = '*';
253			pcb.addr.hci_node[1] = 0;
254		}
255
256		fprintf(stdout,
257"%-8lx %-8lx %-6.6x %6d %6d %-16.16s\n",
258			(unsigned long) pcb.so,
259			(unsigned long) this,
260			pcb.flags,
261			so.so_rcv.sb_ccc,
262			so.so_snd.sb_ccc,
263			pcb.addr.hci_node);
264	}
265} /* hcirawpr */
266
267/*
268 * Print raw L2CAP sockets
269 */
270
271static void
272l2caprawpr(kvm_t *kvmd, u_long addr)
273{
274	ng_btsocket_l2cap_raw_pcb_p	this = NULL, next = NULL;
275	ng_btsocket_l2cap_raw_pcb_t	pcb;
276	struct socket			so;
277	int				first = 1;
278
279	if (addr == 0)
280		return;
281
282        if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0)
283		return;
284
285	for ( ; this != NULL; this = next) {
286		if (kread(kvmd, (u_long) this, (char *) &pcb, sizeof(pcb)) < 0)
287			return;
288		if (kread(kvmd, (u_long) pcb.so, (char *) &so, sizeof(so)) < 0)
289			return;
290
291		next = LIST_NEXT(&pcb, next);
292
293		if (first) {
294			first = 0;
295			fprintf(stdout,
296"Active raw L2CAP sockets\n" \
297"%-8.8s %-8.8s %-6.6s %-6.6s %-17.17s\n",
298				"Socket",
299				"PCB",
300				"Recv-Q",
301				"Send-Q",
302				"Local address");
303		}
304
305		fprintf(stdout,
306"%-8lx %-8lx %6d %6d %-17.17s\n",
307			(unsigned long) pcb.so,
308			(unsigned long) this,
309			so.so_rcv.sb_ccc,
310			so.so_snd.sb_ccc,
311			bdaddrpr(&pcb.src, NULL, 0));
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[24], remote[24];
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		fprintf(stdout,
365"%-8lx %6d %6d %-17.17s/%-5d %-17.17s %-5d %s\n",
366			(unsigned long) this,
367			so.so_rcv.sb_ccc,
368			so.so_snd.sb_ccc,
369			bdaddrpr(&pcb.src, local, sizeof(local)),
370			pcb.psm,
371			bdaddrpr(&pcb.dst, remote, sizeof(remote)),
372			pcb.cid,
373			(so.so_options & SO_ACCEPTCONN)?
374				"LISTEN" : state2str(pcb.state));
375	}
376} /* l2cappr */
377
378/*
379 * Print L2CAP routing table
380 */
381
382static void
383l2caprtpr(kvm_t *kvmd, u_long addr)
384{
385	ng_btsocket_l2cap_rtentry_p	this = NULL, next = NULL;
386	ng_btsocket_l2cap_rtentry_t	rt;
387	int				first = 1;
388
389	if (addr == 0)
390		return;
391
392	if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0)
393		return;
394
395	for ( ; this != NULL; this = next) {
396		if (kread(kvmd, (u_long) this, (char *) &rt, sizeof(rt)) < 0)
397			return;
398
399		next = LIST_NEXT(&rt, next);
400
401		if (first) {
402			first = 0;
403			fprintf(stdout,
404"Known %sL2CAP routes\n", (addr == nl[N_L2CAP_RAW_RT].n_value)?  "raw " : "");
405			fprintf(stdout,
406"%-8.8s %-8.8s %-17.17s\n",	"RTentry",
407				"Hook",
408				"BD_ADDR");
409		}
410
411		fprintf(stdout,
412"%-8lx %-8lx %-17.17s\n",
413			(unsigned long) this,
414			(unsigned long) rt.hook,
415			bdaddrpr(&rt.src, NULL, 0));
416	}
417} /* l2caprtpr */
418
419/*
420 * Print RFCOMM sockets
421 */
422
423static void
424rfcommpr(kvm_t *kvmd, u_long addr)
425{
426	static char const * const	states[] = {
427	/* NG_BTSOCKET_RFCOMM_DLC_CLOSED */	   "CLOSED",
428	/* NG_BTSOCKET_RFCOMM_DLC_W4_CONNECT */	   "W4CON",
429	/* NG_BTSOCKET_RFCOMM_DLC_CONFIGURING */   "CONFIG",
430	/* NG_BTSOCKET_RFCOMM_DLC_CONNECTING */    "CONN",
431	/* NG_BTSOCKET_RFCOMM_DLC_CONNECTED */     "OPEN",
432	/* NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING */ "DISCON"
433	};
434
435	ng_btsocket_rfcomm_pcb_p	this = NULL, next = NULL;
436	ng_btsocket_rfcomm_pcb_t	pcb;
437	struct socket			so;
438	int				first = 1;
439	char				local[24], remote[24];
440
441	if (addr == 0)
442		return;
443
444        if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0)
445		return;
446
447	for ( ; this != NULL; this = next) {
448		if (kread(kvmd, (u_long) this, (char *) &pcb, sizeof(pcb)) < 0)
449			return;
450		if (kread(kvmd, (u_long) pcb.so, (char *) &so, sizeof(so)) < 0)
451			return;
452
453		next = LIST_NEXT(&pcb, next);
454
455		if (first) {
456			first = 0;
457			fprintf(stdout,
458"Active RFCOMM sockets\n" \
459"%-8.8s %-6.6s %-6.6s %-17.17s %-17.17s %-4.4s %-4.4s %s\n",
460				"PCB",
461				"Recv-Q",
462				"Send-Q",
463				"Local address",
464				"Foreign address",
465				"Chan",
466				"DLCI",
467				"State");
468		}
469
470		fprintf(stdout,
471"%-8lx %6d %6d %-17.17s %-17.17s %-4d %-4d %s\n",
472			(unsigned long) this,
473			so.so_rcv.sb_ccc,
474			so.so_snd.sb_ccc,
475			bdaddrpr(&pcb.src, local, sizeof(local)),
476			bdaddrpr(&pcb.dst, remote, sizeof(remote)),
477			pcb.channel,
478			pcb.dlci,
479			(so.so_options & SO_ACCEPTCONN)?
480				"LISTEN" : state2str(pcb.state));
481	}
482} /* rfcommpr */
483
484/*
485 * Print RFCOMM sessions
486 */
487
488static void
489rfcommpr_s(kvm_t *kvmd, u_long addr)
490{
491	static char const * const	states[] = {
492	/* NG_BTSOCKET_RFCOMM_SESSION_CLOSED */	       "CLOSED",
493	/* NG_BTSOCKET_RFCOMM_SESSION_LISTENING */     "LISTEN",
494	/* NG_BTSOCKET_RFCOMM_SESSION_CONNECTING */    "CONNECTING",
495	/* NG_BTSOCKET_RFCOMM_SESSION_CONNECTED */     "CONNECTED",
496	/* NG_BTSOCKET_RFCOMM_SESSION_OPEN */          "OPEN",
497	/* NG_BTSOCKET_RFCOMM_SESSION_DISCONNECTING */ "DISCONNECTING"
498	};
499
500	ng_btsocket_rfcomm_session_p	this = NULL, next = NULL;
501	ng_btsocket_rfcomm_session_t	s;
502	struct socket			so;
503	int				first = 1;
504
505	if (addr == 0)
506		return;
507
508        if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0)
509		return;
510
511	for ( ; this != NULL; this = next) {
512		if (kread(kvmd, (u_long) this, (char *) &s, sizeof(s)) < 0)
513			return;
514		if (kread(kvmd, (u_long) s.l2so, (char *) &so, sizeof(so)) < 0)
515			return;
516
517		next = LIST_NEXT(&s, next);
518
519		if (first) {
520			first = 0;
521			fprintf(stdout,
522"Active RFCOMM sessions\n" \
523"%-8.8s %-8.8s %-4.4s %-5.5s %-5.5s %-4.4s %s\n",
524				"L2PCB",
525				"PCB",
526				"Flags",
527				"MTU",
528				"Out-Q",
529				"DLCs",
530				"State");
531		}
532
533		fprintf(stdout,
534"%-8lx %-8lx %-4x %-5d %-5d %-4s %s\n",
535			(unsigned long) so.so_pcb,
536			(unsigned long) this,
537			s.flags,
538			s.mtu,
539			s.outq.len,
540			LIST_EMPTY(&s.dlcs)? "No" : "Yes",
541			state2str(s.state));
542	}
543} /* rfcommpr_s */
544
545/*
546 * Return BD_ADDR as string
547 */
548
549static char *
550bdaddrpr(bdaddr_p const ba, char *str, int len)
551{
552	static char	 buffer[MAXHOSTNAMELEN];
553	struct hostent	*he = NULL;
554
555	if (str == NULL) {
556		str = buffer;
557		len = sizeof(buffer);
558	}
559
560	if (memcmp(ba, NG_HCI_BDADDR_ANY, sizeof(*ba)) == 0) {
561		str[0] = '*';
562		str[1] = 0;
563
564		return (str);
565	}
566
567	if (!numeric_bdaddr &&
568	    (he = bt_gethostbyaddr((char *)ba, sizeof(*ba), AF_BLUETOOTH)) != NULL) {
569		strlcpy(str, he->h_name, len);
570
571		return (str);
572	}
573
574	bt_ntoa(ba, str);
575
576	return (str);
577} /* bdaddrpr */
578
579/*
580 * Open kvm
581 */
582
583static kvm_t *
584kopen(char const *memf)
585{
586	kvm_t	*kvmd = NULL;
587	char	 errbuf[_POSIX2_LINE_MAX];
588
589	kvmd = kvm_openfiles(NULL, memf, NULL, O_RDONLY, errbuf);
590	if (setgid(getgid()) != 0)
591		err(1, "setgid");
592	if (kvmd == NULL) {
593		warnx("kvm_openfiles: %s", errbuf);
594		return (NULL);
595	}
596
597	if (kvm_nlist(kvmd, nl) < 0) {
598		warnx("kvm_nlist: %s", kvm_geterr(kvmd));
599		goto fail;
600	}
601
602	if (nl[0].n_type == 0) {
603		warnx("kvm_nlist: no namelist");
604		goto fail;
605	}
606
607	return (kvmd);
608fail:
609	kvm_close(kvmd);
610
611	return (NULL);
612} /* kopen */
613
614/*
615 * Read kvm
616 */
617
618static int
619kread(kvm_t *kvmd, u_long addr, char *buffer, int size)
620{
621	if (kvmd == NULL || buffer == NULL)
622		return (-1);
623
624	if (kvm_read(kvmd, addr, buffer, size) != size) {
625		warnx("kvm_read: %s", kvm_geterr(kvmd));
626		return (-1);
627	}
628
629	return (0);
630} /* kread */
631
632/*
633 * Print usage and exit
634 */
635
636static void
637usage(void)
638{
639	fprintf(stdout, "Usage: btsockstat [-M core ] [-n] [-p proto] [-r]\n");
640	exit(255);
641} /* usage */
642
643