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