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