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.2 2002/09/16 19:40:14 max Exp $ 29 * $FreeBSD$ 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 <bitstring.h> 43 #include <err.h> 44 #include <fcntl.h> 45 #include <kvm.h> 46 #include <limits.h> 47 48 #include <ng_hci.h> 49 #include <ng_l2cap.h> 50 #include <ng_btsocket.h> 51 #include <ng_btsocket_hci_raw.h> 52 #include <ng_btsocket_l2cap.h> 53 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <unistd.h> 58 59 static void hcirawpr (kvm_t *kvmd, u_long addr); 60 static void l2caprawpr (kvm_t *kvmd, u_long addr); 61 static void l2cappr (kvm_t *kvmd, u_long addr); 62 static void l2caprtpr (kvm_t *kvmd, u_long addr); 63 64 static kvm_t * kopen (char const *memf); 65 static int kread (kvm_t *kvmd, u_long addr, char *buffer, int size); 66 67 static void usage (void); 68 69 /* 70 * List of symbols 71 */ 72 73 static struct nlist nl[] = { 74 #define N_HCI_RAW 0 75 { "_ng_btsocket_hci_raw_sockets" }, 76 #define N_L2CAP_RAW 1 77 { "_ng_btsocket_l2cap_raw_sockets" }, 78 #define N_L2CAP 2 79 { "_ng_btsocket_l2cap_sockets" }, 80 #define N_L2CAP_RAW_RT 3 81 { "_ng_btsocket_l2cap_raw_rt" }, 82 #define N_L2CAP_RT 4 83 { "_ng_btsocket_l2cap_rt" }, 84 { "" }, 85 }; 86 87 /* 88 * Main 89 */ 90 91 int 92 main(int argc, char *argv[]) 93 { 94 int opt, proto = -1, route = 0; 95 kvm_t *kvmd = NULL; 96 char *memf = NULL; 97 98 while ((opt = getopt(argc, argv, "hM:p:r")) != -1) { 99 switch (opt) { 100 case 'M': 101 memf = optarg; 102 break; 103 104 case 'p': 105 if (strcasecmp(optarg, "hci_raw") == 0) 106 proto = N_HCI_RAW; 107 else if (strcasecmp(optarg, "l2cap_raw") == 0) 108 proto = N_L2CAP_RAW; 109 else if (strcasecmp(optarg, "l2cap") == 0) 110 proto = N_L2CAP; 111 else 112 usage(); 113 /* NOT REACHED */ 114 break; 115 116 case 'r': 117 route = 1; 118 break; 119 120 case 'h': 121 default: 122 usage(); 123 /* NOT REACHED */ 124 } 125 } 126 127 if (proto == N_HCI_RAW && route) 128 usage(); 129 /* NOT REACHED */ 130 131 kvmd = kopen(memf); 132 if (kvmd == NULL) 133 return (1); 134 135 switch (proto) { 136 case N_HCI_RAW: 137 hcirawpr(kvmd, nl[N_HCI_RAW].n_value); 138 break; 139 140 case N_L2CAP_RAW: 141 if (route) 142 l2caprtpr(kvmd, nl[N_L2CAP_RAW_RT].n_value); 143 else 144 l2caprawpr(kvmd, nl[N_L2CAP_RAW].n_value); 145 break; 146 147 case N_L2CAP: 148 if (route) 149 l2caprtpr(kvmd, nl[N_L2CAP_RT].n_value); 150 else 151 l2cappr(kvmd, nl[N_L2CAP].n_value); 152 break; 153 154 default: 155 if (route) { 156 l2caprtpr(kvmd, nl[N_L2CAP_RAW_RT].n_value); 157 l2caprtpr(kvmd, nl[N_L2CAP_RT].n_value); 158 } else { 159 hcirawpr(kvmd, nl[N_HCI_RAW].n_value); 160 l2caprawpr(kvmd, nl[N_L2CAP_RAW].n_value); 161 l2cappr(kvmd, nl[N_L2CAP].n_value); 162 } 163 break; 164 } 165 166 return (kvm_close(kvmd)); 167 } /* main */ 168 169 /* 170 * Print raw HCI sockets 171 */ 172 173 static void 174 hcirawpr(kvm_t *kvmd, u_long addr) 175 { 176 ng_btsocket_hci_raw_pcb_p this = NULL, next = NULL; 177 ng_btsocket_hci_raw_pcb_t pcb; 178 struct socket so; 179 int first = 1; 180 181 if (addr == 0) 182 return; 183 184 if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0) 185 return; 186 187 for ( ; this != NULL; this = next) { 188 if (kread(kvmd, (u_long) this, (char *) &pcb, sizeof(pcb)) < 0) 189 return; 190 if (kread(kvmd, (u_long) pcb.so, (char *) &so, sizeof(so)) < 0) 191 return; 192 193 next = LIST_NEXT(&pcb, next); 194 195 if (first) { 196 first = 0; 197 fprintf(stdout, 198 "Active raw HCI sockets\n" \ 199 "%-8.8s %-8.8s %-6.6s %-6.6s %-6.6s %-16.16s\n", 200 "Socket", 201 "PCB", 202 "Flags", 203 "Recv-Q", 204 "Send-Q", 205 "Local address"); 206 } 207 208 if (pcb.addr.hci_node[0] == 0) { 209 pcb.addr.hci_node[0] = '*'; 210 pcb.addr.hci_node[1] = 0; 211 } 212 213 fprintf(stdout, 214 "%-8.8x %-8.8x %-6.6x %6d %6d %-16.16s\n", 215 (int) pcb.so, 216 (int) this, 217 pcb.flags, 218 so.so_rcv.sb_cc, 219 so.so_snd.sb_cc, 220 pcb.addr.hci_node); 221 } 222 } /* hcirawpr */ 223 224 /* 225 * Print raw L2CAP sockets 226 */ 227 228 static void 229 l2caprawpr(kvm_t *kvmd, u_long addr) 230 { 231 ng_btsocket_l2cap_raw_pcb_p this = NULL, next = NULL; 232 ng_btsocket_l2cap_raw_pcb_t pcb; 233 struct socket so; 234 int first = 1; 235 char bdaddr[32]; 236 237 if (addr == 0) 238 return; 239 240 if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0) 241 return; 242 243 for ( ; this != NULL; this = next) { 244 if (kread(kvmd, (u_long) this, (char *) &pcb, sizeof(pcb)) < 0) 245 return; 246 if (kread(kvmd, (u_long) pcb.so, (char *) &so, sizeof(so)) < 0) 247 return; 248 249 next = LIST_NEXT(&pcb, next); 250 251 if (first) { 252 first = 0; 253 fprintf(stdout, 254 "Active raw L2CAP sockets\n" \ 255 "%-8.8s %-8.8s %-6.6s %-6.6s %-18.18s\n", 256 "Socket", 257 "PCB", 258 "Recv-Q", 259 "Send-Q", 260 "Local address"); 261 } 262 263 if (memcmp(&pcb.src, NG_HCI_BDADDR_ANY, sizeof(pcb.src)) == 0) { 264 bdaddr[0] = '*'; 265 bdaddr[1] = 0; 266 } else 267 snprintf(bdaddr, sizeof(bdaddr), 268 "%02x:%02x:%02x:%02x:%02x:%02x", 269 pcb.src.b[5], pcb.src.b[4], pcb.src.b[3], 270 pcb.src.b[2], pcb.src.b[1], pcb.src.b[0]); 271 272 fprintf(stdout, 273 "%-8.8x %-8.8x %6d %6d %-18.18s\n", 274 (int) pcb.so, 275 (int) this, 276 so.so_rcv.sb_cc, 277 so.so_snd.sb_cc, 278 bdaddr); 279 } 280 } /* l2caprawpr */ 281 282 /* 283 * Print L2CAP sockets 284 */ 285 286 static void 287 l2cappr(kvm_t *kvmd, u_long addr) 288 { 289 static char const * const states[] = { 290 /* NG_BTSOCKET_L2CAP_CLOSED */ "CLOSED", 291 /* NG_BTSOCKET_L2CAP_CONNECTING */ "CON", 292 /* NG_BTSOCKET_L2CAP_CONFIGURING */ "CONFIG", 293 /* NG_BTSOCKET_L2CAP_OPEN */ "OPEN", 294 /* NG_BTSOCKET_L2CAP_DISCONNECTING */ "DISCON" 295 }; 296 #define state2str(x) \ 297 (((x) >= sizeof(states)/sizeof(states[0]))? "UNKNOWN" : states[(x)]) 298 299 ng_btsocket_l2cap_pcb_p this = NULL, next = NULL; 300 ng_btsocket_l2cap_pcb_t pcb; 301 struct socket so; 302 int first = 1; 303 char local[32], remote[32]; 304 305 306 if (addr == 0) 307 return; 308 309 if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0) 310 return; 311 312 for ( ; this != NULL; this = next) { 313 if (kread(kvmd, (u_long) this, (char *) &pcb, sizeof(pcb)) < 0) 314 return; 315 if (kread(kvmd, (u_long) pcb.so, (char *) &so, sizeof(so)) < 0) 316 return; 317 318 next = LIST_NEXT(&pcb, next); 319 320 if (first) { 321 first = 0; 322 fprintf(stdout, 323 "Active L2CAP sockets\n" \ 324 "%-8.8s %-6.6s %-6.6s %-24.24s %-18.18s %-5.5s %s\n", 325 "PCB", 326 "Recv-Q", 327 "Send-Q", 328 "Local address/PSM", 329 "Foreign address", 330 "CID", 331 "State"); 332 } 333 334 if (memcmp(&pcb.src, NG_HCI_BDADDR_ANY, sizeof(pcb.src)) == 0) 335 snprintf(local, sizeof(local), "*/%d", pcb.psm); 336 else 337 snprintf(local, sizeof(local), 338 "%02x:%02x:%02x:%02x:%02x:%02x/%d", 339 pcb.src.b[5], pcb.src.b[4], pcb.src.b[3], 340 pcb.src.b[2], pcb.src.b[1], pcb.src.b[0], 341 pcb.psm); 342 343 if (memcmp(&pcb.dst, NG_HCI_BDADDR_ANY, sizeof(pcb.dst)) == 0) { 344 remote[0] = '*'; 345 remote[1] = 0; 346 } else 347 snprintf(remote, sizeof(remote), 348 "%02x:%02x:%02x:%02x:%02x:%02x", 349 pcb.dst.b[5], pcb.dst.b[4], pcb.dst.b[3], 350 pcb.dst.b[2], pcb.dst.b[1], pcb.dst.b[0]); 351 352 fprintf(stdout, 353 "%-8.8x %6d %6d %-24.24s %-18.18s %-5d %s\n", 354 (int) this, 355 so.so_rcv.sb_cc, 356 so.so_snd.sb_cc, 357 local, 358 remote, 359 pcb.cid, 360 (so.so_options & SO_ACCEPTCONN)? 361 "LISTEN" : state2str(pcb.state)); 362 } 363 } /* l2cappr */ 364 365 /* 366 * Print L2CAP routing table 367 */ 368 369 static void 370 l2caprtpr(kvm_t *kvmd, u_long addr) 371 { 372 ng_btsocket_l2cap_rtentry_p this = NULL, next = NULL; 373 ng_btsocket_l2cap_rtentry_t rt; 374 int first = 1; 375 char bdaddr[32]; 376 377 if (addr == 0) 378 return; 379 380 if (kread(kvmd, addr, (char *) &this, sizeof(this)) < 0) 381 return; 382 383 for ( ; this != NULL; this = next) { 384 if (kread(kvmd, (u_long) this, (char *) &rt, sizeof(rt)) < 0) 385 return; 386 387 next = LIST_NEXT(&rt, next); 388 389 if (first) { 390 first = 0; 391 fprintf(stdout, 392 "Known %sL2CAP routes\n", (addr == nl[N_L2CAP_RAW_RT].n_value)? "raw " : ""); 393 fprintf(stdout, 394 "%-8.8s %-8.8s %-18.18s\n", "RTentry", 395 "Hook", 396 "BD_ADDR"); 397 } 398 399 if (memcmp(&rt.src, NG_HCI_BDADDR_ANY, sizeof(rt.src)) == 0) { 400 bdaddr[0] = '-'; 401 bdaddr[1] = 0; 402 } else 403 snprintf(bdaddr, sizeof(bdaddr), 404 "%02x:%02x:%02x:%02x:%02x:%02x", rt.src.b[5], rt.src.b[4], rt.src.b[3], 405 rt.src.b[2], rt.src.b[1], rt.src.b[0]); 406 407 fprintf(stdout, 408 "%-8.8x %-8.8x %-18.18s\n", 409 (int) this, 410 (int) rt.hook, 411 bdaddr); 412 } 413 } /* l2caprtpr */ 414 415 /* 416 * Open kvm 417 */ 418 419 static kvm_t * 420 kopen(char const *memf) 421 { 422 kvm_t *kvmd = NULL; 423 char errbuf[_POSIX2_LINE_MAX]; 424 425 /* 426 * Discard setgid privileges if not the running kernel so that 427 * bad guys can't print interesting stuff from kernel memory. 428 */ 429 430 if (memf != NULL) 431 setgid(getgid()); 432 433 kvmd = kvm_openfiles(NULL, memf, NULL, O_RDONLY, errbuf); 434 if (kvmd == NULL) { 435 warnx("kvm_openfiles: %s", errbuf); 436 return (NULL); 437 } 438 439 if (kvm_nlist(kvmd, nl) < 0) { 440 warnx("kvm_nlist: %s", kvm_geterr(kvmd)); 441 goto fail; 442 } 443 444 if (nl[0].n_type == 0) { 445 warnx("kvm_nlist: no namelist"); 446 goto fail; 447 } 448 449 return (kvmd); 450 fail: 451 kvm_close(kvmd); 452 453 return (NULL); 454 } /* kopen */ 455 456 /* 457 * Read kvm 458 */ 459 460 static int 461 kread(kvm_t *kvmd, u_long addr, char *buffer, int size) 462 { 463 if (kvmd == NULL || buffer == NULL) 464 return (-1); 465 466 if (kvm_read(kvmd, addr, buffer, size) != size) { 467 warnx("kvm_read: %s", kvm_geterr(kvmd)); 468 return (-1); 469 } 470 471 return (0); 472 } /* kread */ 473 474 /* 475 * Print usage and exit 476 */ 477 478 static void 479 usage(void) 480 { 481 fprintf(stdout, "Usage: btsockstat [-M core ] [-p proto] [-r]\n"); 482 exit(255); 483 } /* usage */ 484 485