1 /* $NetBSD: netstat.c,v 1.27 2005/02/26 22:12:33 dsl Exp $ */
4 * Copyright (c) 1980, 1992, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/cdefs.h>
35 static char sccsid
[] = "@(#)netstat.c 8.1 (Berkeley) 6/6/93";
37 __RCSID("$NetBSD: netstat.c,v 1.27 2005/02/26 22:12:33 dsl Exp $");
43 #include <sys/param.h>
44 #include <sys/socketvar.h>
46 #include <sys/protosw.h>
48 #include <netinet/in.h>
50 #include <arpa/inet.h>
51 #include <net/route.h>
53 #include <netinet/in_systm.h>
54 #include <netinet/ip.h>
55 #include <netinet/in_pcb.h>
56 #include <netinet/ip_icmp.h>
57 #include <netinet/icmp_var.h>
58 #include <netinet/ip_var.h>
60 #include <netinet/ip6.h>
61 #include <netinet6/in6_pcb.h>
63 #include <netinet/tcp.h>
64 #include <netinet/tcpip.h>
65 #include <netinet/tcp_seq.h>
67 #include <netinet/tcp_fsm.h>
68 #include <netinet/tcp_timer.h>
69 #include <netinet/tcp_var.h>
70 #include <netinet/tcp_debug.h>
71 #include <netinet/udp.h>
72 #include <netinet/udp_var.h>
81 static void fetchnetstat4(void *, int);
82 static void enter(struct inpcb
*, struct socket
*, int, const char *);
83 static const char *inetname(struct in_addr
);
84 static void inetprint(struct in_addr
*, int, const char *);
86 static void fetchnetstat6(void *, int);
87 static void enter6(struct in6pcb
*, struct socket
*, int, const char *);
88 static const char *inet6name(struct in6_addr
*);
89 static void inet6print(struct in6_addr
*, int, const char *);
92 #define streq(a,b) (strcmp(a,b)==0)
95 struct netinfo
*ni_forw
, *ni_prev
;
97 short ni_line
; /* line on screen */
98 short ni_seen
; /* 0 when not present in list */
100 #define NIF_LACHG 0x1 /* local address changed */
101 #define NIF_FACHG 0x2 /* foreign address changed */
102 short ni_state
; /* tcp state */
103 const char *ni_proto
; /* protocol */
104 struct in_addr ni_laddr
; /* local address */
106 struct in6_addr ni_laddr6
; /* local address */
108 long ni_lport
; /* local port */
109 struct in_addr ni_faddr
; /* foreign address */
111 struct in6_addr ni_faddr6
; /* foreign address */
113 long ni_fport
; /* foreign port */
114 long ni_rcvcc
; /* rcv buffer character count */
115 long ni_sndcc
; /* snd buffer character count */
119 struct netinfo
*ni_forw
, *ni_prev
;
122 struct netinfo
*nhead
;
124 static int aflag
= 0;
126 static int lastrow
= 1;
134 return (subwin(stdscr
, -1, 0, 5, 0));
138 closenetstat(WINDOW
*w
)
146 if (p
->ni_line
!= -1)
158 static struct nlist namelist
[] = {
160 { .n_name
= "_tcbtable" },
162 { .n_name
= "_udbtable" },
171 n
= kvm_nlist(kd
, namelist
);
175 } else if (n
== sizeof(namelist
) / sizeof(namelist
[0]) - 1) {
176 error("No symbols in namelist");
180 nhead
= (struct netinfo
*)(void *)&netcb
;
182 netcb
.ni_forw
= netcb
.ni_prev
= nhead
;
192 if (namelist
[X_TCBTABLE
].n_value
== 0)
194 for (p
= netcb
.ni_forw
; p
!= nhead
; p
= p
->ni_forw
)
197 if ((protos
& (TCP
| UDP
)) == 0) {
198 error("No protocols to display");
201 if ((protos
& TCP
) && namelist
[X_TCBTABLE
].n_type
)
202 fetchnetstat4(NPTR(X_TCBTABLE
), 1);
203 if ((protos
& UDP
) && namelist
[X_UDBTABLE
].n_type
)
204 fetchnetstat4(NPTR(X_UDBTABLE
), 0);
206 if ((protos
& TCP
) && namelist
[X_TCBTABLE
].n_type
)
207 fetchnetstat6(NPTR(X_TCBTABLE
), 1);
208 if ((protos
& UDP
) && namelist
[X_UDBTABLE
].n_type
)
209 fetchnetstat6(NPTR(X_UDBTABLE
), 0);
214 fetchnetstat4(void *off
, int istcp
)
216 struct inpcbtable pcbtable
;
217 struct inpcb
*head
, *prev
, *next
;
223 KREAD(off
, &pcbtable
, sizeof pcbtable
);
224 prev
= head
= (struct inpcb
*)&((struct inpcbtable
*)off
)->inpt_queue
;
225 next
= (struct inpcb
*)pcbtable
.inpt_queue
.cqh_first
;
226 while (next
!= head
) {
227 KREAD(next
, &inpcb
, sizeof (inpcb
));
228 if ((struct inpcb
*)inpcb
.inp_queue
.cqe_prev
!= prev
) {
229 for (p
= netcb
.ni_forw
; p
!= nhead
; p
= p
->ni_forw
)
231 error("Kernel state in transition");
235 next
= (struct inpcb
*)inpcb
.inp_queue
.cqe_next
;
237 if (inpcb
.inp_af
!= AF_INET
)
239 if (!aflag
&& inet_lnaof(inpcb
.inp_laddr
) == INADDR_ANY
)
241 if (nhosts
&& !checkhost(&inpcb
))
243 if (nports
&& !checkport(&inpcb
))
245 KREAD(inpcb
.inp_socket
, &sockb
, sizeof (sockb
));
247 KREAD(inpcb
.inp_ppcb
, &tcpcb
, sizeof (tcpcb
));
248 enter(&inpcb
, &sockb
, tcpcb
.t_state
, "tcp");
250 enter(&inpcb
, &sockb
, 0, "udp");
256 fetchnetstat6(void *off
, int istcp
)
258 struct inpcbtable pcbtable
;
259 struct in6pcb
*head6
, *prev6
, *next6
;
263 struct in6pcb in6pcb
;
265 KREAD(off
, &pcbtable
, sizeof pcbtable
);
266 prev6
= head6
= (struct in6pcb
*)&((struct inpcbtable
*)off
)->inpt_queue
;
267 next6
= (struct in6pcb
*)pcbtable
.inpt_queue
.cqh_first
;
268 while (next6
!= head6
) {
269 KREAD(next6
, &in6pcb
, sizeof (in6pcb
));
270 if ((struct in6pcb
*)in6pcb
.in6p_queue
.cqe_prev
!= prev6
) {
271 for (p
= netcb
.ni_forw
; p
!= nhead
; p
= p
->ni_forw
)
273 error("Kernel state in transition");
277 next6
= (struct in6pcb
*)in6pcb
.in6p_queue
.cqe_next
;
279 if (in6pcb
.in6p_af
!= AF_INET6
)
281 if (!aflag
&& IN6_IS_ADDR_UNSPECIFIED(&in6pcb
.in6p_laddr
))
283 if (nhosts
&& !checkhost6(&in6pcb
))
285 if (nports
&& !checkport6(&in6pcb
))
287 KREAD(in6pcb
.in6p_socket
, &sockb
, sizeof (sockb
));
289 KREAD(in6pcb
.in6p_ppcb
, &tcpcb
, sizeof (tcpcb
));
290 enter6(&in6pcb
, &sockb
, tcpcb
.t_state
, "tcp");
292 enter6(&in6pcb
, &sockb
, 0, "udp");
298 enter(struct inpcb
*inp
, struct socket
*so
, int state
, const char *proto
)
303 * Only take exact matches, any sockets with
304 * previously unbound addresses will be deleted
305 * below in the display routine because they
306 * will appear as ``not seen'' in the kernel
309 for (p
= netcb
.ni_forw
; p
!= nhead
; p
= p
->ni_forw
) {
310 if (p
->ni_family
!= AF_INET
)
312 if (!streq(proto
, p
->ni_proto
))
314 if (p
->ni_lport
!= inp
->inp_lport
||
315 p
->ni_laddr
.s_addr
!= inp
->inp_laddr
.s_addr
)
317 if (p
->ni_faddr
.s_addr
== inp
->inp_faddr
.s_addr
&&
318 p
->ni_fport
== inp
->inp_fport
)
322 if ((p
= malloc(sizeof(*p
))) == NULL
) {
323 error("Out of memory");
327 p
->ni_forw
= netcb
.ni_forw
;
328 netcb
.ni_forw
->ni_prev
= p
;
331 p
->ni_laddr
= inp
->inp_laddr
;
332 p
->ni_lport
= inp
->inp_lport
;
333 p
->ni_faddr
= inp
->inp_faddr
;
334 p
->ni_fport
= inp
->inp_fport
;
336 p
->ni_flags
= NIF_LACHG
| NIF_FACHG
;
337 p
->ni_family
= AF_INET
;
339 p
->ni_rcvcc
= so
->so_rcv
.sb_cc
;
340 p
->ni_sndcc
= so
->so_snd
.sb_cc
;
347 enter6(struct in6pcb
*in6p
, struct socket
*so
, int state
, const char *proto
)
352 * Only take exact matches, any sockets with
353 * previously unbound addresses will be deleted
354 * below in the display routine because they
355 * will appear as ``not seen'' in the kernel
358 for (p
= netcb
.ni_forw
; p
!= nhead
; p
= p
->ni_forw
) {
359 if (p
->ni_family
!= AF_INET6
)
361 if (!streq(proto
, p
->ni_proto
))
363 if (p
->ni_lport
!= in6p
->in6p_lport
||
364 !IN6_ARE_ADDR_EQUAL(&p
->ni_laddr6
, &in6p
->in6p_laddr
))
366 if (IN6_ARE_ADDR_EQUAL(&p
->ni_faddr6
, &in6p
->in6p_faddr
) &&
367 p
->ni_fport
== in6p
->in6p_fport
)
371 if ((p
= malloc(sizeof(*p
))) == NULL
) {
372 error("Out of memory");
376 p
->ni_forw
= netcb
.ni_forw
;
377 netcb
.ni_forw
->ni_prev
= p
;
380 p
->ni_laddr6
= in6p
->in6p_laddr
;
381 p
->ni_lport
= in6p
->in6p_lport
;
382 p
->ni_faddr6
= in6p
->in6p_faddr
;
383 p
->ni_fport
= in6p
->in6p_fport
;
385 p
->ni_flags
= NIF_LACHG
| NIF_FACHG
;
386 p
->ni_family
= AF_INET6
;
388 p
->ni_rcvcc
= so
->so_rcv
.sb_cc
;
389 p
->ni_sndcc
= so
->so_snd
.sb_cc
;
395 /* column locations */
397 #define FADDR LADDR+23
398 #define PROTO FADDR+23
399 #define RCVCC PROTO+6
400 #define SNDCC RCVCC+7
401 #define STATE SNDCC+7
408 if (namelist
[X_TCBTABLE
].n_type
== 0)
410 wmove(wnd
, 0, 0); wclrtobot(wnd
);
411 mvwaddstr(wnd
, 0, LADDR
, "Local Address");
412 mvwaddstr(wnd
, 0, FADDR
, "Foreign Address");
413 mvwaddstr(wnd
, 0, PROTO
, "Proto");
414 mvwaddstr(wnd
, 0, RCVCC
, "Recv-Q");
415 mvwaddstr(wnd
, 0, SNDCC
, "Send-Q");
416 mvwaddstr(wnd
, 0, STATE
, "(state)");
418 for (p
= netcb
.ni_forw
; p
!= nhead
; p
= p
->ni_forw
) {
419 if (p
->ni_line
== -1)
421 p
->ni_flags
|= NIF_LACHG
| NIF_FACHG
;
428 struct netinfo
*p
, *q
;
431 * First, delete any connections that have gone
432 * away and adjust the position of connections
433 * below to reflect the deleted line.
437 if (p
->ni_line
== -1 || p
->ni_seen
) {
441 wmove(wnd
, p
->ni_line
, 0);
443 for (q
= netcb
.ni_forw
; q
!= nhead
; q
= q
->ni_forw
)
444 if (q
!= p
&& q
->ni_line
> p
->ni_line
) {
446 /* this shouldn't be necessary */
447 q
->ni_flags
|= NIF_LACHG
| NIF_FACHG
;
451 p
->ni_prev
->ni_forw
= p
->ni_forw
;
452 p
->ni_forw
->ni_prev
= p
->ni_prev
;
457 * Update existing connections and add new ones.
459 for (p
= netcb
.ni_forw
; p
!= nhead
; p
= p
->ni_forw
) {
460 if (p
->ni_line
== -1) {
462 * Add a new entry if possible.
464 if (lastrow
> getmaxy(wnd
))
466 p
->ni_line
= lastrow
++;
467 p
->ni_flags
|= NIF_LACHG
| NIF_FACHG
;
469 if (p
->ni_flags
& NIF_LACHG
) {
470 wmove(wnd
, p
->ni_line
, LADDR
);
471 switch (p
->ni_family
) {
473 inetprint(&p
->ni_laddr
, p
->ni_lport
,
478 inet6print(&p
->ni_laddr6
, p
->ni_lport
,
483 p
->ni_flags
&= ~NIF_LACHG
;
485 if (p
->ni_flags
& NIF_FACHG
) {
486 wmove(wnd
, p
->ni_line
, FADDR
);
487 switch (p
->ni_family
) {
489 inetprint(&p
->ni_faddr
, p
->ni_fport
,
494 inet6print(&p
->ni_faddr6
, p
->ni_fport
,
499 p
->ni_flags
&= ~NIF_FACHG
;
501 mvwaddstr(wnd
, p
->ni_line
, PROTO
, p
->ni_proto
);
503 if (p
->ni_family
== AF_INET6
)
506 mvwprintw(wnd
, p
->ni_line
, RCVCC
, "%6ld", p
->ni_rcvcc
);
507 mvwprintw(wnd
, p
->ni_line
, SNDCC
, "%6ld", p
->ni_sndcc
);
508 if (streq(p
->ni_proto
, "tcp")) {
509 if (p
->ni_state
< 0 || p
->ni_state
>= TCP_NSTATES
)
510 mvwprintw(wnd
, p
->ni_line
, STATE
, "%d",
513 mvwaddstr(wnd
, p
->ni_line
, STATE
,
514 tcpstates
[p
->ni_state
]);
518 if (lastrow
< getmaxy(wnd
)) {
519 wmove(wnd
, lastrow
, 0); wclrtobot(wnd
);
520 wmove(wnd
, getmaxy(wnd
), 0); wdeleteln(wnd
); /* XXX */
525 * Pretty print an Internet address (net address + port).
526 * If the nflag was specified, use numbers instead of names.
529 inetprint(struct in_addr
*in
, int port
, const char *proto
)
531 struct servent
*sp
= 0;
534 (void)snprintf(line
, sizeof line
, "%.*s.", 16, inetname(*in
));
535 cp
= strchr(line
, '\0');
537 sp
= getservbyport(port
, proto
);
539 (void)snprintf(cp
, line
+ sizeof line
- cp
, "%.8s",
540 sp
? sp
->s_name
: "*");
542 (void)snprintf(cp
, line
+ sizeof line
- cp
, "%d",
543 ntohs((u_short
)port
));
544 /* pad to full column to clear any garbage */
545 cp
= strchr(line
, '\0');
546 while (cp
- line
< 22)
554 inet6print(struct in6_addr
*in6
, int port
, const char *proto
)
556 struct servent
*sp
= 0;
559 (void)snprintf(line
, sizeof line
, "%.*s.", 16, inet6name(in6
));
560 cp
= strchr(line
, '\0');
562 sp
= getservbyport(port
, proto
);
564 (void)snprintf(cp
, line
+ sizeof line
- cp
, "%.8s",
565 sp
? sp
->s_name
: "*");
567 (void)snprintf(cp
, line
+ sizeof line
- cp
, "%d",
568 ntohs((u_short
)port
));
569 /* pad to full column to clear any garbage */
570 cp
= strchr(line
, '\0');
571 while (cp
- line
< 22)
579 * Construct an Internet address representation.
580 * If the nflag has been supplied, give
581 * numeric value, otherwise try for symbolic name.
584 inetname(struct in_addr in
)
587 static char line
[50];
591 if (!nflag
&& in
.s_addr
!= INADDR_ANY
) {
592 int net
= inet_netof(in
);
593 int lna
= inet_lnaof(in
);
595 if (lna
== INADDR_ANY
) {
596 np
= getnetbyaddr(net
, AF_INET
);
601 hp
= gethostbyaddr((char *)&in
, sizeof (in
), AF_INET
);
606 if (in
.s_addr
== INADDR_ANY
)
607 strlcpy(line
, "*", sizeof(line
));
609 strlcpy(line
, cp
, sizeof(line
));
611 in
.s_addr
= ntohl(in
.s_addr
);
612 #define C(x) ((x) & 0xff)
613 (void)snprintf(line
, sizeof line
, "%u.%u.%u.%u",
614 C(in
.s_addr
>> 24), C(in
.s_addr
>> 16),
615 C(in
.s_addr
>> 8), C(in
.s_addr
));
623 inet6name(struct in6_addr
*in6
)
625 static char line
[NI_MAXHOST
];
626 struct sockaddr_in6 sin6
;
630 flags
= NI_NUMERICHOST
;
633 if (IN6_IS_ADDR_UNSPECIFIED(in6
))
635 memset(&sin6
, 0, sizeof(sin6
));
636 sin6
.sin6_family
= AF_INET6
;
637 sin6
.sin6_len
= sizeof(struct sockaddr_in6
);
638 sin6
.sin6_addr
= *in6
;
639 if (getnameinfo((struct sockaddr
*)&sin6
, sin6
.sin6_len
,
640 line
, sizeof(line
), NULL
, 0, flags
) == 0)
646 /* please note: there are also some netstat commands in netcmds.c */
649 netstat_all(char *args
)
658 netstat_names(char *args
)
672 netstat_numbers(char *args
)