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