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 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 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 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 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 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 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 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 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 * 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 * 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 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 637 usage(void) 638 { 639 fprintf(stdout, "Usage: btsockstat [-M core ] [-n] [-p proto] [-r]\n"); 640 exit(255); 641 } /* usage */ 642 643