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