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: stable/12/usr.bin/bluetooth/btsockstat/btsockstat.c 326276 2017-11-27 15:37:16Z pfg $
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
63 static void hcirawpr (kvm_t *kvmd, u_long addr);
64 static void l2caprawpr (kvm_t *kvmd, u_long addr);
65 static void l2cappr (kvm_t *kvmd, u_long addr);
66 static void l2caprtpr (kvm_t *kvmd, u_long addr);
67 static void rfcommpr (kvm_t *kvmd, u_long addr);
68 static void rfcommpr_s (kvm_t *kvmd, u_long addr);
69
70 static char * bdaddrpr (bdaddr_p const ba, char *str, int len);
71
72 static kvm_t * kopen (char const *memf);
73 static int kread (kvm_t *kvmd, u_long addr, char *buffer, int size);
74
75 static void usage (void);
76
77 /*
78 * List of symbols
79 */
80
81 static 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
106 static int numeric_bdaddr = 0;
107
108 int
main(int argc,char * argv[])109 main(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
216 static void
hcirawpr(kvm_t * kvmd,u_long addr)217 hcirawpr(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
271 static void
l2caprawpr(kvm_t * kvmd,u_long addr)272 l2caprawpr(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
319 static void
l2cappr(kvm_t * kvmd,u_long addr)320 l2cappr(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
382 static void
l2caprtpr(kvm_t * kvmd,u_long addr)383 l2caprtpr(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
423 static void
rfcommpr(kvm_t * kvmd,u_long addr)424 rfcommpr(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
488 static void
rfcommpr_s(kvm_t * kvmd,u_long addr)489 rfcommpr_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
549 static char *
bdaddrpr(bdaddr_p const ba,char * str,int len)550 bdaddrpr(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
583 static kvm_t *
kopen(char const * memf)584 kopen(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);
608 fail:
609 kvm_close(kvmd);
610
611 return (NULL);
612 } /* kopen */
613
614 /*
615 * Read kvm
616 */
617
618 static int
kread(kvm_t * kvmd,u_long addr,char * buffer,int size)619 kread(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
636 static void
usage(void)637 usage(void)
638 {
639 fprintf(stdout, "Usage: btsockstat [-M core ] [-n] [-p proto] [-r]\n");
640 exit(255);
641 } /* usage */
642
643