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