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