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