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