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 
42 #define L2CAP_SOCKET_CHECKED
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 
60 static void	hcirawpr   (kvm_t *kvmd, u_long addr);
61 static void	l2caprawpr (kvm_t *kvmd, u_long addr);
62 static void	l2cappr    (kvm_t *kvmd, u_long addr);
63 static void	l2caprtpr  (kvm_t *kvmd, u_long addr);
64 static void	rfcommpr   (kvm_t *kvmd, u_long addr);
65 static void	rfcommpr_s (kvm_t *kvmd, u_long addr);
66 
67 static char *	bdaddrpr   (bdaddr_p const ba, char *str, int len);
68 
69 static kvm_t *	kopen      (char const *memf);
70 static int	kread      (kvm_t *kvmd, u_long addr, char *buffer, int size);
71 
72 static void	usage      (void);
73 
74 /*
75  * List of symbols
76  */
77 
78 static 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 
103 static int	numeric_bdaddr = 0;
104 
105 int
106 main(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 	if (memf != NULL)
158 		if (setgid(getgid()) != 0)
159 			err(1, "setgid");
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 
213 static void
214 hcirawpr(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_ccc,
259 			so.so_snd.sb_ccc,
260 			pcb.addr.hci_node);
261 	}
262 } /* hcirawpr */
263 
264 /*
265  * Print raw L2CAP sockets
266  */
267 
268 static void
269 l2caprawpr(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_ccc,
307 			so.so_snd.sb_ccc,
308 			bdaddrpr(&pcb.src, NULL, 0));
309 	}
310 } /* l2caprawpr */
311 
312 /*
313  * Print L2CAP sockets
314  */
315 
316 static void
317 l2cappr(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_ccc,
365 			so.so_snd.sb_ccc,
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 
379 static void
380 l2caprtpr(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 
420 static void
421 rfcommpr(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_ccc,
471 			so.so_snd.sb_ccc,
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 
485 static void
486 rfcommpr_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 
546 static char *
547 bdaddrpr(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 
580 static kvm_t *
581 kopen(char const *memf)
582 {
583 	kvm_t	*kvmd = NULL;
584 	char	 errbuf[_POSIX2_LINE_MAX];
585 
586 	kvmd = kvm_openfiles(NULL, memf, NULL, O_RDONLY, errbuf);
587 	if (setgid(getgid()) != 0)
588 		err(1, "setgid");
589 	if (kvmd == NULL) {
590 		warnx("kvm_openfiles: %s", errbuf);
591 		return (NULL);
592 	}
593 
594 	if (kvm_nlist(kvmd, nl) < 0) {
595 		warnx("kvm_nlist: %s", kvm_geterr(kvmd));
596 		goto fail;
597 	}
598 
599 	if (nl[0].n_type == 0) {
600 		warnx("kvm_nlist: no namelist");
601 		goto fail;
602 	}
603 
604 	return (kvmd);
605 fail:
606 	kvm_close(kvmd);
607 
608 	return (NULL);
609 } /* kopen */
610 
611 /*
612  * Read kvm
613  */
614 
615 static int
616 kread(kvm_t *kvmd, u_long addr, char *buffer, int size)
617 {
618 	if (kvmd == NULL || buffer == NULL)
619 		return (-1);
620 
621 	if (kvm_read(kvmd, addr, buffer, size) != size) {
622 		warnx("kvm_read: %s", kvm_geterr(kvmd));
623 		return (-1);
624 	}
625 
626 	return (0);
627 } /* kread */
628 
629 /*
630  * Print usage and exit
631  */
632 
633 static void
634 usage(void)
635 {
636 	fprintf(stdout, "Usage: btsockstat [-M core ] [-n] [-p proto] [-r]\n");
637 	exit(255);
638 } /* usage */
639 
640