1 /* $NetBSD: sockstat.c,v 1.14 2008/04/29 06:53:03 martin Exp $ */
4 * Copyright (c) 2005 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/cdefs.h>
34 __RCSID("$NetBSD: sockstat.c,v 1.14 2008/04/29 06:53:03 martin Exp $");
37 #include <sys/types.h>
38 #include <sys/param.h>
39 #include <sys/sysctl.h>
40 #include <sys/socket.h>
41 #include <sys/socketvar.h>
43 #include <netinet/in.h>
44 #include <net/route.h>
45 #include <netinet/in_systm.h>
46 #include <netinet/ip.h>
47 #include <netinet/in_pcb.h>
48 #include <netinet/in_pcb_hdr.h>
49 #include <netinet/tcp_fsm.h>
52 /* want DTYPE_* defines */
56 #include <arpa/inet.h>
58 #include <bitstring.h>
70 #define satosun(sa) ((struct sockaddr_un *)(sa))
71 #define satosin(sa) ((struct sockaddr_in *)(sa))
73 #define satosin6(sa) ((struct sockaddr_in6 *)(sa))
76 void parse_ports(const char *);
77 int get_num(const char *, const char **, const char **);
78 void get_sockets(const char *);
80 int sort_files(const void *, const void *);
81 void sysctl_sucker(int *, u_int
, void **, size_t *);
82 void socket_add_hash(struct kinfo_pcb
*, int);
83 int isconnected(struct kinfo_pcb
*);
84 int islistening(struct kinfo_pcb
*);
85 struct kinfo_pcb
*pick_socket(struct kinfo_file
*);
86 int get_proc(struct kinfo_proc2
*, int);
87 int print_socket(struct kinfo_file
*, struct kinfo_pcb
*,
88 struct kinfo_proc2
*);
89 void print_addr(int, int, int, struct sockaddr
*);
91 LIST_HEAD(socklist
, sockitem
);
93 struct socklist sockhash
[HASHSIZE
];
95 LIST_ENTRY(sockitem
) s_list
;
96 struct kinfo_pcb
*s_sock
;
99 struct kinfo_file
*flist
;
102 int pf_list
, only
, nonames
;
105 #define PF_LIST_INET 1
107 #define PF_LIST_INET6 2
109 #define PF_LIST_LOCAL 4
110 #define ONLY_CONNECTED 1
111 #define ONLY_LISTEN 2
114 main(int argc
, char *argv
[])
116 struct kinfo_pcb
*kp
;
119 struct kinfo_proc2 p
;
124 while ((ch
= getopt(argc
, argv
, "46cf:lnp:u")) != - 1) {
126 while ((ch
= getopt(argc
, argv
, "4cf:lnp:u")) != - 1) {
130 pf_list
|= PF_LIST_INET
;
134 pf_list
|= PF_LIST_INET6
;
138 only
|= ONLY_CONNECTED
;
141 if (strcasecmp(optarg
, "inet") == 0)
142 pf_list
|= PF_LIST_INET
;
144 else if (strcasecmp(optarg
, "inet6") == 0)
145 pf_list
|= PF_LIST_INET6
;
147 else if (strcasecmp(optarg
, "local") == 0)
148 pf_list
|= PF_LIST_LOCAL
;
149 else if (strcasecmp(optarg
, "unix") == 0)
150 pf_list
|= PF_LIST_LOCAL
;
152 errx(1, "%s: unsupported protocol family",
165 pf_list
|= PF_LIST_LOCAL
;
175 if ((portmap
!= NULL
) && (pf_list
== 0)) {
176 pf_list
= PF_LIST_INET
;
178 pf_list
|= PF_LIST_INET6
;
182 pf_list
= PF_LIST_INET
| PF_LIST_LOCAL
;
184 pf_list
|= PF_LIST_INET6
;
187 if ((portmap
!= NULL
) && (pf_list
& PF_LIST_LOCAL
))
188 errx(1, "local domain sockets do not have ports");
190 if (pf_list
& PF_LIST_INET
) {
191 get_sockets("net.inet.tcp.pcblist");
192 get_sockets("net.inet.udp.pcblist");
194 get_sockets("net.inet.raw.pcblist");
198 if (pf_list
& PF_LIST_INET6
) {
199 get_sockets("net.inet6.tcp6.pcblist");
200 get_sockets("net.inet6.udp6.pcblist");
202 get_sockets("net.inet6.raw6.pcblist");
206 if (pf_list
& PF_LIST_LOCAL
) {
207 get_sockets("net.local.stream.pcblist");
208 get_sockets("net.local.dgram.pcblist");
214 for (i
= 0; i
< flistc
; i
++)
215 if ((kp
= pick_socket(&flist
[i
])) != NULL
&&
216 get_proc(&p
, flist
[i
].ki_pid
) == 0)
217 print_socket(&flist
[i
], kp
, &p
);
223 parse_ports(const char *l
)
229 if (portmap
== NULL
) {
230 portmap
= bit_alloc(65536);
235 if ((srv
= getservbyname(l
, NULL
)) != NULL
) {
236 bit_set(portmap
, ntohs(srv
->s_port
));
242 i
= get_num(l
, &s
, &e
);
252 j
= get_num(l
, &s
, &e
);
264 get_num(const char *l
, const char **s
, const char **e
)
269 while (isdigit((u_int
)**e
))
273 x
= strtol(*s
, &t
, 0);
274 if (errno
== 0 && x
>= 0 && x
<= 65535 && t
== *e
)
283 get_sockets(const char *mib
)
287 int rc
, n
, name
[CTL_MAXNAME
];
291 rc
= sysctlnametomib(mib
, &name
[0], &sz
);
295 err(1, "sysctlnametomib: %s", mib
);
299 name
[namelen
++] = PCB_ALL
;
300 name
[namelen
++] = 0; /* XXX all pids */
301 name
[namelen
++] = sizeof(struct kinfo_pcb
);
302 name
[namelen
++] = INT_MAX
; /* all of them */
304 sysctl_sucker(&name
[0], namelen
, &v
, &sz
);
305 n
= sz
/ sizeof(struct kinfo_pcb
);
306 socket_add_hash(v
, n
);
314 int rc
, name
[CTL_MAXNAME
];
318 rc
= sysctlnametomib("kern.file2", &name
[0], &sz
);
320 err(1, "sysctlnametomib");
323 name
[namelen
++] = KERN_FILE_BYPID
;
324 name
[namelen
++] = 0; /* XXX all pids */
325 name
[namelen
++] = sizeof(struct kinfo_file
);
326 name
[namelen
++] = INT_MAX
; /* all of them */
328 sysctl_sucker(&name
[0], namelen
, &v
, &sz
);
330 flistc
= sz
/ sizeof(struct kinfo_file
);
332 qsort(flist
, flistc
, sizeof(*flist
), sort_files
);
336 sort_files(const void *a
, const void *b
)
338 const struct kinfo_file
*ka
= a
, *kb
= b
;
340 if (ka
->ki_pid
== kb
->ki_pid
)
341 return (ka
->ki_fd
- kb
->ki_fd
);
343 return (ka
->ki_pid
- kb
->ki_pid
);
347 sysctl_sucker(int *name
, u_int namelen
, void **vp
, size_t *szp
)
353 /* printf("name %p, namelen %u\n", name, namelen); */
358 rc
= sysctl(&name
[0], namelen
, v
, &sz
, NULL
, 0);
359 if (rc
== -1 && errno
!= ENOMEM
)
361 if (rc
== -1 && v
!= NULL
) {
375 /* printf("got %zu at %p\n", sz, v); */
379 socket_add_hash(struct kinfo_pcb
*kp
, int n
)
387 si
= malloc(sizeof(*si
) * n
);
391 for (i
= 0; i
< n
; i
++) {
392 si
[i
].s_sock
= &kp
[i
];
393 hash
= (int)(kp
[i
].ki_sockaddr
% HASHSIZE
);
394 LIST_INSERT_HEAD(&sockhash
[hash
], &si
[i
], s_list
);
399 isconnected(struct kinfo_pcb
*kp
)
402 if ((kp
->ki_sostate
& SS_ISCONNECTED
) ||
403 (kp
->ki_prstate
>= INP_CONNECTED
) ||
404 (kp
->ki_tstate
> TCPS_LISTEN
) ||
412 islistening(struct kinfo_pcb
*kp
)
418 if (kp
->ki_tstate
== TCPS_LISTEN
)
421 switch (kp
->ki_family
) {
423 if (kp
->ki_type
== SOCK_RAW
||
424 (kp
->ki_type
== SOCK_DGRAM
&&
425 ntohs(satosin(&kp
->ki_src
)->sin_port
) != 0))
430 if (kp
->ki_type
== SOCK_RAW
||
431 (kp
->ki_type
== SOCK_DGRAM
&&
432 ntohs(satosin6(&kp
->ki_src
)->sin6_port
) != 0))
437 if (satosun(&kp
->ki_src
)->sun_path
[0] != '\0')
448 pick_socket(struct kinfo_file
*f
)
451 struct kinfo_pcb
*kp
;
454 if (f
->ki_ftype
!= DTYPE_SOCKET
)
457 hash
= (int)(f
->ki_fdata
% HASHSIZE
);
458 LIST_FOREACH(si
, &sockhash
[hash
], s_list
) {
459 if (si
->s_sock
->ki_sockaddr
== f
->ki_fdata
)
468 if (isconnected(kp
)) {
470 * connected but you didn't say you wanted
473 if (!(only
& ONLY_CONNECTED
))
476 else if (islistening(kp
)) {
478 * listening but you didn't ask for listening
481 if (!(only
& ONLY_LISTEN
))
486 * neither connected nor listening, so you
493 switch (kp
->ki_family
) {
495 if (!bit_test(portmap
,
496 ntohs(satosin(&kp
->ki_src
)->sin_port
)) &&
498 ntohs(satosin(&kp
->ki_dst
)->sin_port
)))
503 if (!bit_test(portmap
,
504 ntohs(satosin6(&kp
->ki_src
)->sin6_port
)) &&
506 ntohs(satosin6(&kp
->ki_dst
)->sin6_port
)))
519 get_proc(struct kinfo_proc2
*p
, int pid
)
530 name
[namelen
++] = CTL_KERN
;
531 name
[namelen
++] = KERN_PROC2
;
532 name
[namelen
++] = KERN_PROC_PID
;
533 name
[namelen
++] = pid
;
534 name
[namelen
++] = sz
;
537 return (sysctl(&name
[0], namelen
, p
, &sz
, NULL
, 0));
541 print_socket(struct kinfo_file
*kf
, struct kinfo_pcb
*kp
, struct kinfo_proc2
*p
)
543 static int first
= 1;
549 printf("%-8s " "%-10s " "%-5s " "%-2s " "%-6s "
551 "USER", "COMMAND", "PID", "FD", "PROTO",
552 "LOCAL ADDRESS", "FOREIGN ADDRESS");
556 if ((pw
= getpwuid(p
->p_uid
)) != NULL
)
557 printf("%-8s ", pw
->pw_name
);
559 printf("%-8d ", (int)p
->p_uid
);
561 printf("%-10.10s ", p
->p_comm
);
562 printf("%-5d ", (int)kf
->ki_pid
);
563 printf("%2d ", (int)kf
->ki_fd
);
565 snprintf(proto
, sizeof(proto
), "%d/%d", kp
->ki_family
, kp
->ki_protocol
);
567 switch (kp
->ki_family
) {
569 switch (kp
->ki_protocol
) {
570 case IPPROTO_TCP
: t
= "tcp"; break;
571 case IPPROTO_UDP
: t
= "udp"; break;
572 case IPPROTO_RAW
: t
= "raw"; break;
573 default: t
= proto
; break;
578 switch (kp
->ki_protocol
) {
579 case IPPROTO_TCP
: t
= "tcp6"; break;
580 case IPPROTO_UDP
: t
= "udp6"; break;
581 case IPPROTO_RAW
: t
= "raw6"; break;
582 default: t
= proto
; break;
587 switch (kp
->ki_type
) {
588 case SOCK_STREAM
: t
= "stream"; break;
589 case SOCK_DGRAM
: t
= "dgram"; break;
590 case SOCK_RAW
: t
= "raw"; break;
591 case SOCK_RDM
: t
= "rdm"; break;
592 case SOCK_SEQPACKET
: t
= "seq"; break;
593 default: t
= proto
; break;
597 snprintf(proto
, sizeof(proto
), "%d/%d/%d",
598 kp
->ki_family
, kp
->ki_type
, kp
->ki_protocol
);
606 if (kp->ki_family == PF_LOCAL) {
607 if (kp->ki_src.sa_len > 2) {
608 print_addr(0, kp->ki_type, kp->ki_pflags, &kp->ki_src);
609 if (kp->ki_dst.sa_family == PF_LOCAL)
612 if (kp->ki_dst.sa_family == PF_LOCAL)
616 print_addr(21, kp
->ki_type
, kp
->ki_pflags
, &kp
->ki_src
);
621 print_addr(0, kp
->ki_type
, kp
->ki_pflags
, &kp
->ki_dst
);
622 else if (kp
->ki_family
== PF_INET
624 || kp
->ki_family
== PF_INET6
627 printf("%-*s", 0, "*.*");
628 /* else if (kp->ki_src.sa_len == 2)
629 printf("%-*s", 0, "-"); */
639 print_addr(int l
, int t
, int f
, struct sockaddr
*sa
)
641 char sabuf
[256], pbuf
[32];
644 if (!(f
& INP_ANONPORT
))
651 f
|= NI_NUMERICHOST
|NI_NUMERICSERV
;
653 getnameinfo(sa
, sa
->sa_len
, sabuf
, sizeof(sabuf
),
654 pbuf
, sizeof(pbuf
), f
);
656 switch (sa
->sa_family
) {
658 r
= printf("(PF_UNSPEC)");
661 struct sockaddr_in
*si
= satosin(sa
);
662 if (si
->sin_addr
.s_addr
!= INADDR_ANY
)
663 r
= printf("%s.%s", sabuf
, pbuf
);
664 else if (ntohs(si
->sin_port
) != 0)
665 r
= printf("*.%s", pbuf
);
672 struct sockaddr_in6
*si6
= satosin6(sa
);
673 if (!IN6_IS_ADDR_UNSPECIFIED(&si6
->sin6_addr
))
674 r
= printf("%s.%s", sabuf
, pbuf
);
675 else if (ntohs(si6
->sin6_port
) != 0)
676 r
= printf("*.%s", pbuf
);
683 struct sockaddr_un
*sun
= satosun(sa
);
684 r
= printf("%s", sun
->sun_path
);
696 printf("%*s", l
, "");