2 * Copyright (c) 2005 Joerg Sonnenberger <joerg@bec.de>. All rights reserved.
3 * Copyright (c) 2002 Dag-Erling Coïdan Smørgrav
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer
11 * in this position and unchanged.
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 * 3. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 * $FreeBSD: src/usr.bin/sockstat/sockstat.c,v 1.12 2004/12/06 09:28:05 ru Exp $
33 #include <sys/param.h>
34 #include <sys/socket.h>
35 #include <sys/socketvar.h>
36 #include <sys/sysctl.h>
40 #include <sys/unpcb.h>
42 #include <net/route.h>
44 #include <netinet/in.h>
45 #include <netinet/in_pcb.h>
46 #include <netinet/tcp.h>
47 #include <netinet/tcp_seq.h>
48 #include <netinet/tcp_var.h>
49 #include <arpa/inet.h>
63 static int opt_4
; /* Show IPv4 sockets */
64 static int opt_6
; /* Show IPv6 sockets */
65 static int opt_c
; /* Show connected sockets */
66 static int opt_l
; /* Show listening sockets */
67 static int opt_u
; /* Show Unix domain sockets */
68 static int opt_v
; /* Verbose mode */
72 #define INT_BIT (sizeof(int)*CHAR_BIT)
73 #define SET_PORT(p) do { ports[p / INT_BIT] |= 1 << (p % INT_BIT); } while (0)
74 #define CHK_PORT(p) (ports[p / INT_BIT] & (1 << (p % INT_BIT)))
81 const char *protoname
;
82 struct sockaddr_storage laddr
;
83 struct sockaddr_storage faddr
;
87 static int xprintf(const char *, ...) __printflike(1, 2);
90 static struct sock
*sockhash
[HASHSIZE
];
92 static struct kinfo_file
*xfiles
;
93 static size_t nxfiles
;
96 xprintf(const char *fmt
, ...)
102 len
= vprintf(fmt
, ap
);
110 parse_ports(const char *portspec
)
116 if ((ports
= calloc(65536 / INT_BIT
, sizeof(int))) == NULL
)
121 errx(1, "syntax error in port range");
122 for (q
= p
; *q
!= '\0' && isdigit(*q
); ++q
)
124 for (port
= 0; p
< q
; ++p
)
125 port
= port
* 10 + (*p
- '0');
126 if (port
< 0 || port
> 65535)
127 errx(1, "invalid port number");
140 for (q
= p
; *q
!= '\0' && isdigit(*q
); ++q
)
142 for (end
= 0; p
< q
; ++p
)
143 end
= end
* 10 + (*p
- '0');
144 if (end
< port
|| end
> 65535)
145 errx(1, "invalid port number");
154 sockaddr(struct sockaddr_storage
*sa
, int af
, void *addr
, int port
)
156 struct sockaddr_in
*sin4
;
157 struct sockaddr_in6
*sin6
;
159 bzero(sa
, sizeof *sa
);
162 sin4
= (struct sockaddr_in
*)sa
;
163 sin4
->sin_len
= sizeof *sin4
;
164 sin4
->sin_family
= af
;
165 sin4
->sin_port
= port
;
166 sin4
->sin_addr
= *(struct in_addr
*)addr
;
169 sin6
= (struct sockaddr_in6
*)sa
;
170 sin6
->sin6_len
= sizeof *sin6
;
171 sin6
->sin6_family
= af
;
172 sin6
->sin6_port
= port
;
173 sin6
->sin6_addr
= *(struct in6_addr
*)addr
;
181 gather_inet(int proto
)
183 void *so_begin
, *so_end
;
189 const char *varname
, *protoname
;
196 varname
= "net.inet.tcp.pcblist";
200 varname
= "net.inet.udp.pcblist";
204 varname
= "net.inet.divert.pcblist";
214 if (sysctlbyname(varname
, NULL
, &len
, NULL
, 0)) {
217 err(1, "fetching %s", varname
);
219 if ((buf
= malloc(len
)) == NULL
)
221 if (sysctlbyname(varname
, buf
, &len
, NULL
, 0)) {
224 err(1, "fetching %s", varname
);
228 so_end
= (uint8_t *)buf
+ len
;
230 for (so_begin
= buf
, so_end
= (uint8_t *)so_begin
+ len
;
231 (uint8_t *)so_begin
+ sizeof(size_t) < (uint8_t *)so_end
&&
232 (uint8_t *)so_begin
+ *(size_t *)so_begin
<= (uint8_t *)so_end
;
233 so_begin
= (uint8_t *)so_begin
+ *(size_t *)so_begin
) {
236 xtp
= (struct xtcpcb
*)so_begin
;
237 if (xtp
->xt_len
!= sizeof *xtp
) {
238 warnx("struct xtcpcb size mismatch");
242 so
= &xtp
->xt_socket
;
246 xip
= (struct xinpcb
*)so_begin
;
247 if (xip
->xi_len
!= sizeof *xip
) {
248 warnx("struct xinpcb size mismatch");
252 so
= &xip
->xi_socket
;
257 if ((INP_ISIPV4(inp
) && !opt_4
) || (INP_ISIPV6(inp
) && !opt_6
))
259 if (INP_ISIPV4(inp
)) {
260 if ((inp
->inp_fport
== 0 && !opt_l
) ||
261 (inp
->inp_fport
!= 0 && !opt_c
))
263 } else if (INP_ISIPV6(inp
)) {
264 if ((inp
->in6p_fport
== 0 && !opt_l
) ||
265 (inp
->in6p_fport
!= 0 && !opt_c
))
269 warnx("invalid af 0x%x", inp
->inp_af
);
272 if ((sock
= calloc(1, sizeof *sock
)) == NULL
)
274 sock
->socket
= so
->xso_so
;
276 if (INP_ISIPV4(inp
)) {
277 sock
->family
= AF_INET
;
278 sockaddr(&sock
->laddr
, sock
->family
,
279 &inp
->inp_laddr
, inp
->inp_lport
);
280 sockaddr(&sock
->faddr
, sock
->family
,
281 &inp
->inp_faddr
, inp
->inp_fport
);
282 } else if (INP_ISIPV6(inp
)) {
283 sock
->family
= AF_INET6
;
284 sockaddr(&sock
->laddr
, sock
->family
,
285 &inp
->in6p_laddr
, inp
->in6p_lport
);
286 sockaddr(&sock
->faddr
, sock
->family
,
287 &inp
->in6p_faddr
, inp
->in6p_fport
);
289 sock
->protoname
= protoname
;
290 hash
= (int)((uintptr_t)sock
->socket
% HASHSIZE
);
291 sock
->next
= sockhash
[hash
];
292 sockhash
[hash
] = sock
;
299 gather_unix(int proto
)
301 void *so_begin
, *so_end
;
304 const char *varname
, *protoname
;
311 varname
= "net.local.stream.pcblist";
312 protoname
= "stream";
315 varname
= "net.local.dgram.pcblist";
325 if (sysctlbyname(varname
, NULL
, &len
, NULL
, 0))
326 err(1, "fetching %s", varname
);
328 if ((buf
= malloc(len
)) == NULL
)
330 if (sysctlbyname(varname
, buf
, &len
, NULL
, 0))
331 err(1, "fetching %s", varname
);
333 for (so_begin
= buf
, so_end
= (uint8_t *)buf
+ len
;
334 (uint8_t *)so_begin
+ sizeof(size_t) < (uint8_t *)so_end
&&
335 (uint8_t *)so_begin
+ *(size_t *)so_begin
<= (uint8_t *)so_end
;
336 so_begin
= (uint8_t *)so_begin
+ *(size_t *)so_begin
) {
338 if (xup
->xu_len
!= sizeof *xup
) {
339 warnx("struct xunpcb size mismatch");
342 if ((xup
->xu_unp
.unp_conn
== NULL
&& !opt_l
) ||
343 (xup
->xu_unp
.unp_conn
!= NULL
&& !opt_c
))
345 if ((sock
= calloc(1, sizeof *sock
)) == NULL
)
347 sock
->socket
= xup
->xu_socket
.xso_so
;
348 sock
->pcb
= xup
->xu_unpp
;
350 sock
->family
= AF_UNIX
;
351 sock
->protoname
= protoname
;
352 if (xup
->xu_unp
.unp_addr
!= NULL
)
354 *(struct sockaddr_storage
*)(void *)&xup
->xu_addr
;
355 else if (xup
->xu_unp
.unp_conn
!= NULL
)
356 *(void **)&sock
->faddr
= xup
->xu_unp
.unp_conn
;
357 hash
= (int)((uintptr_t)sock
->socket
% HASHSIZE
);
358 sock
->next
= sockhash
[hash
];
359 sockhash
[hash
] = sock
;
368 if (kinfo_get_files(&xfiles
, &nxfiles
))
369 err(1, "kinfo_get_files");
373 printaddr(int af
, struct sockaddr_storage
*ss
)
375 char addrstr
[INET6_ADDRSTRLEN
] = { '\0', '\0' };
376 struct sockaddr_un
*sun
;
382 addr
= &((struct sockaddr_in
*)ss
)->sin_addr
;
383 if (inet_lnaof(*(struct in_addr
*)addr
) == INADDR_ANY
)
385 port
= ntohs(((struct sockaddr_in
*)ss
)->sin_port
);
388 addr
= &((struct sockaddr_in6
*)ss
)->sin6_addr
;
389 if (IN6_IS_ADDR_UNSPECIFIED((struct in6_addr
*)addr
))
391 port
= ntohs(((struct sockaddr_in6
*)ss
)->sin6_port
);
394 sun
= (struct sockaddr_un
*)ss
;
395 off
= (int)((char *)&sun
->sun_path
- (char *)sun
);
396 return (xprintf("%.*s", sun
->sun_len
- off
, sun
->sun_path
));
400 if (addrstr
[0] == '\0')
401 inet_ntop(af
, addr
, addrstr
, sizeof addrstr
);
403 return xprintf("%s:*", addrstr
);
405 return xprintf("%s:%d", addrstr
, port
);
409 getprocname(pid_t pid
)
411 static struct kinfo_proc proc
;
417 mib
[2] = KERN_PROC_PID
;
420 if (sysctl(mib
, 4, &proc
, &len
, NULL
, 0) == -1) {
424 return (proc
.kp_comm
);
428 check_ports(struct sock
*s
)
434 if ((s
->family
!= AF_INET
) && (s
->family
!= AF_INET6
))
436 if (s
->family
== AF_INET
)
437 port
= ntohs(((struct sockaddr_in
*)(&s
->laddr
))->sin_port
);
439 port
= ntohs(((struct sockaddr_in6
*)(&s
->laddr
))->sin6_port
);
442 if (s
->family
== AF_INET
)
443 port
= ntohs(((struct sockaddr_in
*)(&s
->faddr
))->sin_port
);
445 port
= ntohs(((struct sockaddr_in6
*)(&s
->faddr
))->sin6_port
);
455 struct kinfo_file
*xf
;
460 printf("%-8s %-10s %-5s %-2s %-6s %-21s %-21s\n",
461 "USER", "COMMAND", "PID", "FD", "PROTO",
462 "LOCAL ADDRESS", "FOREIGN ADDRESS");
464 for (xf
= xfiles
, n
= 0; n
< (int)nxfiles
; ++n
, ++xf
) {
465 if (xf
->f_data
== NULL
)
467 hash
= (int)((uintptr_t)xf
->f_data
% HASHSIZE
);
468 for (s
= sockhash
[hash
]; s
!= NULL
; s
= s
->next
)
469 if ((void *)s
->socket
== xf
->f_data
)
476 if ((pwd
= getpwuid(xf
->f_uid
)) == NULL
)
477 pos
+= xprintf("%lu", (u_long
)xf
->f_uid
);
479 pos
+= xprintf("%s", pwd
->pw_name
);
482 pos
+= xprintf("%.10s", getprocname(xf
->f_pid
));
485 pos
+= xprintf("%lu", (u_long
)xf
->f_pid
);
488 pos
+= xprintf("%d", xf
->f_fd
);
491 pos
+= xprintf("%s", s
->protoname
);
492 if (s
->family
== AF_INET
)
494 if (s
->family
== AF_INET6
)
501 pos
+= printaddr(s
->family
, &s
->laddr
);
505 pos
+= printaddr(s
->family
, &s
->faddr
);
509 if (s
->laddr
.ss_len
> 0) {
510 pos
+= printaddr(s
->family
, &s
->laddr
);
514 p
= *(void **)&s
->faddr
;
516 pos
+= xprintf("(not connected)");
519 pos
+= xprintf("-> ");
520 for (hash
= 0; hash
< HASHSIZE
; ++hash
) {
521 for (s
= sockhash
[hash
]; s
!= NULL
; s
= s
->next
)
527 if (s
== NULL
|| s
->laddr
.ss_len
== 0)
528 pos
+= xprintf("??");
530 pos
+= printaddr(s
->family
, &s
->laddr
);
542 fprintf(stderr
, "Usage: sockstat [-46clu] [-p ports]\n");
547 main(int argc
, char *argv
[])
551 while ((o
= getopt(argc
, argv
, "46clp:uv")) != -1)
584 if (!opt_4
&& !opt_6
&& !opt_u
)
585 opt_4
= opt_6
= opt_u
= 1;
586 if (!opt_c
&& !opt_l
)
589 if (opt_4
|| opt_6
) {
590 gather_inet(IPPROTO_TCP
);
591 gather_inet(IPPROTO_UDP
);
592 gather_inet(IPPROTO_DIVERT
);
595 gather_unix(SOCK_STREAM
);
596 gather_unix(SOCK_DGRAM
);