btsockstat.c revision 121064
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: head/usr.bin/bluetooth/btsockstat/btsockstat.c 121064 2003-10-13 05:30:20Z bde $ 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 59static void hcirawpr (kvm_t *kvmd, u_long addr); 60static void l2caprawpr (kvm_t *kvmd, u_long addr); 61static void l2cappr (kvm_t *kvmd, u_long addr); 62static void l2caprtpr (kvm_t *kvmd, u_long addr); 63static void rfcommpr (kvm_t *kvmd, u_long addr); 64static void rfcommpr_s (kvm_t *kvmd, u_long addr); 65 66static char * bdaddrpr (bdaddr_p const ba, char *str, int len); 67 68static kvm_t * kopen (char const *memf); 69static int kread (kvm_t *kvmd, u_long addr, char *buffer, int size); 70 71static void usage (void); 72 73/* 74 * List of symbols 75 */ 76 77static 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 102static int numeric_bdaddr = 0; 103 104int 105main(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 212static void 213hcirawpr(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"%-8.8x %-8.8x %-6.6x %6d %6d %-16.16s\n", 254 (int) pcb.so, 255 (int) 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 267static void 268l2caprawpr(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"%-8.8x %-8.8x %6d %6d %-17.17s\n", 303 (int) pcb.so, 304 (int) 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 315static void 316l2cappr(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"%-8.8x %6d %6d %-17.17s/%-5d %-17.17s %-5d %s\n", 362 (int) 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 378static void 379l2caprtpr(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"%-8.8x %-8.8x %-17.17s\n", 409 (int) this, 410 (int) rt.hook, 411 bdaddrpr(&rt.src, NULL, 0)); 412 } 413} /* l2caprtpr */ 414 415/* 416 * Print RFCOMM sockets 417 */ 418 419static void 420rfcommpr(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"%-8.8x %6d %6d %-17.17s %-17.17s %-4d %-4d %s\n", 468 (int) 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 484static void 485rfcommpr_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"%-8.8x %-8.8x %-4x %-5d %-5d %-4s %s\n", 531 (int) so.so_pcb, 532 (int) 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 545static char * 546bdaddrpr(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 579static kvm_t * 580kopen(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); 610fail: 611 kvm_close(kvmd); 612 613 return (NULL); 614} /* kopen */ 615 616/* 617 * Read kvm 618 */ 619 620static int 621kread(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 638static void 639usage(void) 640{ 641 fprintf(stdout, "Usage: btsockstat [-M core ] [-n] [-p proto] [-r]\n"); 642 exit(255); 643} /* usage */ 644 645