1f9ab90drgrimes/*-
2872b698pfg * SPDX-License-Identifier: BSD-3-Clause
3872b698pfg *
4f9ab90drgrimes * Copyright (c) 1980, 1992, 1993
5f9ab90drgrimes *	The Regents of the University of California.  All rights reserved.
6f9ab90drgrimes *
7f9ab90drgrimes * Redistribution and use in source and binary forms, with or without
8f9ab90drgrimes * modification, are permitted provided that the following conditions
9f9ab90drgrimes * are met:
10f9ab90drgrimes * 1. Redistributions of source code must retain the above copyright
11f9ab90drgrimes *    notice, this list of conditions and the following disclaimer.
12f9ab90drgrimes * 2. Redistributions in binary form must reproduce the above copyright
13f9ab90drgrimes *    notice, this list of conditions and the following disclaimer in the
14f9ab90drgrimes *    documentation and/or other materials provided with the distribution.
157e6cabdimp * 3. Neither the name of the University nor the names of its contributors
16f9ab90drgrimes *    may be used to endorse or promote products derived from this software
17f9ab90drgrimes *    without specific prior written permission.
18f9ab90drgrimes *
19f9ab90drgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20f9ab90drgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21f9ab90drgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22f9ab90drgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23f9ab90drgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24f9ab90drgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25f9ab90drgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26f9ab90drgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27f9ab90drgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28f9ab90drgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29f9ab90drgrimes * SUCH DAMAGE.
30f9ab90drgrimes */
31f9ab90drgrimes
32fa1f2b9markm#include <sys/cdefs.h>
33fa1f2b9markm
34fa1f2b9markm__FBSDID("$FreeBSD$");
35fa1f2b9markm
36fa1f2b9markm#ifdef lint
37fa1f2b9markmstatic const char sccsid[] = "@(#)netstat.c	8.1 (Berkeley) 6/6/93";
38fa1f2b9markm#endif
39f9ab90drgrimes
40f9ab90drgrimes/*
41f9ab90drgrimes * netstat
42f9ab90drgrimes */
43f9ab90drgrimes#include <sys/param.h>
444239068dg#include <sys/queue.h>
45f9ab90drgrimes#include <sys/socket.h>
467168facglebius#define	_WANT_SOCKET
47f9ab90drgrimes#include <sys/socketvar.h>
48f9ab90drgrimes#include <sys/protosw.h>
49f9ab90drgrimes
50f9ab90drgrimes#include <netinet/in.h>
51fdde621peter#include <arpa/inet.h>
52f9ab90drgrimes#include <net/route.h>
53f9ab90drgrimes#include <netinet/in_systm.h>
54f9ab90drgrimes#include <netinet/ip.h>
559902e77dwmalone#ifdef INET6
569902e77dwmalone#include <netinet/ip6.h>
579902e77dwmalone#endif
583a5c9aaglebius#define	_WANT_INPCB
59f9ab90drgrimes#include <netinet/in_pcb.h>
60f9ab90drgrimes#include <netinet/ip_icmp.h>
61f9ab90drgrimes#include <netinet/icmp_var.h>
62f9ab90drgrimes#include <netinet/ip_var.h>
63f9ab90drgrimes#include <netinet/tcp.h>
64f9ab90drgrimes#include <netinet/tcpip.h>
65f9ab90drgrimes#include <netinet/tcp_seq.h>
66f9ab90drgrimes#define TCPSTATES
67f9ab90drgrimes#include <netinet/tcp_fsm.h>
68f9ab90drgrimes#include <netinet/tcp_timer.h>
693a5c9aaglebius#define	_WANT_TCPCB
709487eafglebius#include <netinet/tcp_var.h>
71f9ab90drgrimes#include <netinet/tcp_debug.h>
72f9ab90drgrimes#include <netinet/udp.h>
73f9ab90drgrimes#include <netinet/udp_var.h>
74f9ab90drgrimes
75f9ab90drgrimes#include <netdb.h>
76f9ab90drgrimes#include <nlist.h>
770842b7ddelphij#include <paths.h>
78fa1f2b9markm#include <stdlib.h>
79fa1f2b9markm#include <string.h>
80fa1f2b9markm
81f9ab90drgrimes#include "systat.h"
82f9ab90drgrimes#include "extern.h"
83f9ab90drgrimes
843a5c9aaglebiusstatic struct netinfo *enter(struct in_conninfo *, uint8_t, int, const char *);
850b20191impstatic void enter_kvm(struct inpcb *, struct socket *, int, const char *);
863a5c9aaglebiusstatic void enter_sysctl(struct xinpcb *, struct xsocket *, int, const char *);
870b20191impstatic void fetchnetstat_kvm(void);
880b20191impstatic void fetchnetstat_sysctl(void);
89ba650c0umestatic char *inetname(struct sockaddr *);
90ba650c0umestatic void inetprint(struct sockaddr *, const char *);
91f9ab90drgrimes
92f9ab90drgrimes#define	streq(a,b)	(strcmp(a,b)==0)
93c4ba6d8delphij#define	YMAX(w)		(getmaxy(w)-2)
94f9ab90drgrimes
95f9ab90drgrimesWINDOW *
968f8cb7cdelphijopennetstat(void)
97f9ab90drgrimes{
98f9ab90drgrimes	sethostent(1);
99f9ab90drgrimes	setnetent(1);
1001a76981bde	return (subwin(stdscr, LINES-3-1, 0, MAINWIN_ROW, 0));
101f9ab90drgrimes}
102f9ab90drgrimes
103f9ab90drgrimesstruct netinfo {
1044cc97bcphk	TAILQ_ENTRY(netinfo) chain;
105f9ab90drgrimes	short	ni_line;		/* line on screen */
106f9ab90drgrimes	short	ni_seen;		/* 0 when not present in list */
107f9ab90drgrimes	short	ni_flags;
108f9ab90drgrimes#define	NIF_LACHG	0x1		/* local address changed */
109f9ab90drgrimes#define	NIF_FACHG	0x2		/* foreign address changed */
110f9ab90drgrimes	short	ni_state;		/* tcp state */
111fa1f2b9markm	const char	*ni_proto;		/* protocol */
112ba650c0ume	struct sockaddr_storage ni_lsa;	/* local address */
113ba650c0ume	struct sockaddr_storage	ni_fsa;	/* foreign address */
11486b3836jdp	u_int	ni_rcvcc;		/* rcv buffer character count */
11586b3836jdp	u_int	ni_sndcc;		/* snd buffer character count */
116f9ab90drgrimes};
117f9ab90drgrimes
1184cc97bcphkTAILQ_HEAD(netinfohead, netinfo) netcb = TAILQ_HEAD_INITIALIZER(netcb);
119f9ab90drgrimes
120f9ab90drgrimesstatic	int aflag = 0;
121f9ab90drgrimesstatic	int nflag = 0;
122f9ab90drgrimesstatic	int lastrow = 1;
123f9ab90drgrimes
124f9ab90drgrimesvoid
1258f8cb7cdelphijclosenetstat(WINDOW *w)
126f9ab90drgrimes{
127fa1f2b9markm	struct netinfo *p;
128f9ab90drgrimes
129f9ab90drgrimes	endhostent();
130f9ab90drgrimes	endnetent();
131f2049d4phk	TAILQ_FOREACH(p, &netcb, chain) {
132f9ab90drgrimes		if (p->ni_line != -1)
133f9ab90drgrimes			lastrow--;
134f9ab90drgrimes		p->ni_line = -1;
135f9ab90drgrimes	}
1362f1e586ed	if (w != NULL) {
137f9ab90drgrimes		wclear(w);
138f9ab90drgrimes		wrefresh(w);
139f9ab90drgrimes		delwin(w);
140f9ab90drgrimes	}
141f9ab90drgrimes}
142f9ab90drgrimes
143fa1f2b9markmstatic const char *miblist[] = {
144d6fecactmm	"net.inet.tcp.pcblist",
145c4e6401bde	"net.inet.udp.pcblist"
146d6fecactmm};
147d6fecactmm
148cf3ea4fyarstatic char tcb[] = "tcb", udb[] = "udb";
149cf3ea4fyar
150d6fecactmmstruct nlist namelist[] = {
151f9ab90drgrimes#define	X_TCB	0
152cf3ea4fyar	{ .n_name = tcb },
153f9ab90drgrimes#define	X_UDB	1
154cf3ea4fyar	{ .n_name = udb },
155cf3ea4fyar	{ .n_name = NULL },
156f9ab90drgrimes};
157f9ab90drgrimes
158f9ab90drgrimesint
1598f8cb7cdelphijinitnetstat(void)
160f9ab90drgrimes{
161f9ab90drgrimes	protos = TCP|UDP;
162f9ab90drgrimes	return(1);
163f9ab90drgrimes}
164f9ab90drgrimes
165f9ab90drgrimesvoid
1668f8cb7cdelphijfetchnetstat(void)
167f9ab90drgrimes{
168d6fecactmm	if (use_kvm)
169d6fecactmm		fetchnetstat_kvm();
170d6fecactmm	else
171d6fecactmm		fetchnetstat_sysctl();
172d6fecactmm}
173d6fecactmm
174d6fecactmmstatic void
1758f8cb7cdelphijfetchnetstat_kvm(void)
176d6fecactmm{
177fa1f2b9markm	struct inpcb *next;
178fa1f2b9markm	struct netinfo *p;
17987a5fb2dg	struct inpcbhead head;
180f9ab90drgrimes	struct inpcb inpcb;
181f9ab90drgrimes	struct socket sockb;
182f9ab90drgrimes	struct tcpcb tcpcb;
183f9ab90drgrimes	void *off;
184f9ab90drgrimes	int istcp;
185f9ab90drgrimes
186f9ab90drgrimes	if (namelist[X_TCB].n_value == 0)
187f9ab90drgrimes		return;
1884cc97bcphk	TAILQ_FOREACH(p, &netcb, chain)
189f9ab90drgrimes		p->ni_seen = 0;
190f9ab90drgrimes	if (protos&TCP) {
191a14d555rgrimes		off = NPTR(X_TCB);
192f9ab90drgrimes		istcp = 1;
193f9ab90drgrimes	}
194f9ab90drgrimes	else if (protos&UDP) {
195a14d555rgrimes		off = NPTR(X_UDB);
196f9ab90drgrimes		istcp = 0;
197f9ab90drgrimes	}
198f9ab90drgrimes	else {
199f9ab90drgrimes		error("No protocols to display");
200f9ab90drgrimes		return;
201f9ab90drgrimes	}
202f9ab90drgrimesagain:
20387a5fb2dg	KREAD(off, &head, sizeof (struct inpcbhead));
2044cc97bcphk	LIST_FOREACH(next, &head, inp_list) {
205f9ab90drgrimes		KREAD(next, &inpcb, sizeof (inpcb));
206f2049d4phk		next = &inpcb;
207ba650c0ume		if (!aflag) {
208ba650c0ume			if (inpcb.inp_vflag & INP_IPV4) {
209ba650c0ume				if (inet_lnaof(inpcb.inp_laddr) == INADDR_ANY)
210ba650c0ume					continue;
211ba650c0ume			}
212ba650c0ume#ifdef INET6
213ba650c0ume			else if (inpcb.inp_vflag & INP_IPV6) {
214ba650c0ume				if (memcmp(&inpcb.in6p_laddr,
215ba650c0ume				    &in6addr_any, sizeof(in6addr_any)) == 0)
216ba650c0ume					continue;
217ba650c0ume			}
218ba650c0ume#endif
219ba650c0ume		}
2203a5c9aaglebius		if (nhosts && !checkhost(&inpcb.inp_inc))
221f9ab90drgrimes			continue;
2223a5c9aaglebius		if (nports && !checkport(&inpcb.inp_inc))
223f9ab90drgrimes			continue;
224f9ab90drgrimes		if (istcp) {
225038bfe2rwatson			if (inpcb.inp_flags & INP_TIMEWAIT) {
226d4e4b73silby				bzero(&sockb, sizeof(sockb));
227d4e4b73silby				enter_kvm(&inpcb, &sockb, TCPS_TIME_WAIT,
228d4e4b73silby					 "tcp");
229d4e4b73silby			} else {
230d4e4b73silby				KREAD(inpcb.inp_socket, &sockb,
231d4e4b73silby					sizeof (sockb));
232d4e4b73silby				KREAD(inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb));
233d4e4b73silby				enter_kvm(&inpcb, &sockb, tcpcb.t_state,
234d4e4b73silby					"tcp");
235d4e4b73silby			}
236f9ab90drgrimes		} else
237d6fecactmm			enter_kvm(&inpcb, &sockb, 0, "udp");
238f9ab90drgrimes	}
239f9ab90drgrimes	if (istcp && (protos&UDP)) {
240f9ab90drgrimes		istcp = 0;
241f9ab90drgrimes		off = NPTR(X_UDB);
242f9ab90drgrimes		goto again;
243f9ab90drgrimes	}
244f9ab90drgrimes}
245f9ab90drgrimes
246f9ab90drgrimesstatic void
2478f8cb7cdelphijfetchnetstat_sysctl(void)
248d6fecactmm{
249fa1f2b9markm	struct netinfo *p;
250d6fecactmm	int idx;
251d6fecactmm	struct xinpgen *inpg;
252d6fecactmm	char *cur, *end;
2530ceaba7yar	struct xinpcb *xip = NULL;
2540ceaba7yar	struct xtcpcb *xtp = NULL;
255d6fecactmm	int plen;
256d6fecactmm	size_t lsz;
257d6fecactmm
258d6fecactmm	TAILQ_FOREACH(p, &netcb, chain)
259d6fecactmm		p->ni_seen = 0;
260d6fecactmm	if (protos&TCP) {
261d6fecactmm		idx = 0;
262d6fecactmm	} else if (protos&UDP) {
263d6fecactmm		idx = 1;
264d6fecactmm	} else {
265d6fecactmm		error("No protocols to display");
266d6fecactmm		return;
267d6fecactmm	}
268d6fecactmm
269d6fecactmm	for (;idx < 2; idx++) {
270d6fecactmm		if (idx == 1 && !(protos&UDP))
271d6fecactmm			break;
272d6fecactmm		inpg = (struct xinpgen *)sysctl_dynread(miblist[idx], &lsz);
273d6fecactmm		if (inpg == NULL) {
274d6fecactmm			error("sysctl(%s...) failed", miblist[idx]);
275d6fecactmm			continue;
276d6fecactmm		}
277c4e6401bde		/*
278d6fecactmm		 * We currently do no require a consistent pcb list.
279c4e6401bde		 * Try to be robust in case of struct size changes
280d6fecactmm		 */
281d6fecactmm		cur = ((char *)inpg) + inpg->xig_len;
282d6fecactmm		/* There is also a trailing struct xinpgen */
283d6fecactmm		end = ((char *)inpg) + lsz - inpg->xig_len;
284d6fecactmm		if (end <= cur) {
285d6fecactmm			free(inpg);
286d6fecactmm			continue;
287d6fecactmm		}
288d6fecactmm		if (idx == 0) { /* TCP */
289d6fecactmm			xtp = (struct xtcpcb *)cur;
290d6fecactmm			plen = xtp->xt_len;
291d6fecactmm		} else {
292d6fecactmm			xip = (struct xinpcb *)cur;
293d6fecactmm			plen = xip->xi_len;
294d6fecactmm		}
295d6fecactmm		while (cur + plen <= end) {
296d6fecactmm			if (idx == 0) { /* TCP */
297d6fecactmm				xtp = (struct xtcpcb *)cur;
2983a5c9aaglebius				xip = &xtp->xt_inp;
299d6fecactmm			} else {
300d6fecactmm				xip = (struct xinpcb *)cur;
301d6fecactmm			}
302d6fecactmm			cur += plen;
303d6fecactmm
304ba650c0ume			if (!aflag) {
3053a5c9aaglebius				if (xip->inp_vflag & INP_IPV4) {
3063a5c9aaglebius					if (inet_lnaof(xip->inp_laddr) ==
307ba650c0ume					    INADDR_ANY)
308ba650c0ume						continue;
309ba650c0ume				}
310ba650c0ume#ifdef INET6
3113a5c9aaglebius				else if (xip->inp_vflag & INP_IPV6) {
3123a5c9aaglebius					if (memcmp(&xip->in6p_laddr,
313ba650c0ume					    &in6addr_any, sizeof(in6addr_any))
314ba650c0ume					    == 0)
315ba650c0ume						continue;
316ba650c0ume				}
317ba650c0ume#endif
318ba650c0ume			}
3193a5c9aaglebius			if (nhosts && !checkhost(&xip->inp_inc))
320d6fecactmm				continue;
3213a5c9aaglebius			if (nports && !checkport(&xip->inp_inc))
322d6fecactmm				continue;
3233a5c9aaglebius			if (idx == 0)
3243a5c9aaglebius				enter_sysctl(xip, &xip->xi_socket,
3253a5c9aaglebius				    xtp->t_state, "tcp");
3263a5c9aaglebius			else
3273a5c9aaglebius				enter_sysctl(xip, &xip->xi_socket, 0, "udp");
328d6fecactmm		}
329d6fecactmm		free(inpg);
330d6fecactmm	}
331d6fecactmm}
332d6fecactmm
333d6fecactmmstatic void
3348f8cb7cdelphijenter_kvm(struct inpcb *inp, struct socket *so, int state, const char *proto)
335f9ab90drgrimes{
336fa1f2b9markm	struct netinfo *p;
337f9ab90drgrimes
3383a5c9aaglebius	if ((p = enter(&inp->inp_inc, inp->inp_vflag, state, proto)) != NULL) {
33925da94eglebius		p->ni_rcvcc = so->so_rcv.sb_ccc;
34025da94eglebius		p->ni_sndcc = so->so_snd.sb_ccc;
341d6fecactmm	}
342d6fecactmm}
343d6fecactmm
344d6fecactmmstatic void
3453a5c9aaglebiusenter_sysctl(struct xinpcb *xip, struct xsocket *so, int state,
3463a5c9aaglebius    const char *proto)
347d6fecactmm{
348fa1f2b9markm	struct netinfo *p;
349d6fecactmm
3503a5c9aaglebius	if ((p = enter(&xip->inp_inc, xip->inp_vflag, state, proto)) != NULL) {
351d6fecactmm		p->ni_rcvcc = so->so_rcv.sb_cc;
352d6fecactmm		p->ni_sndcc = so->so_snd.sb_cc;
353d6fecactmm	}
354d6fecactmm}
355d6fecactmm
356d6fecactmmstatic struct netinfo *
3573a5c9aaglebiusenter(struct in_conninfo *inc, uint8_t vflag, int state, const char *proto)
358d6fecactmm{
359fa1f2b9markm	struct netinfo *p;
360ba650c0ume	struct sockaddr_storage lsa, fsa;
361ba650c0ume	struct sockaddr_in *sa4;
362ba650c0ume#ifdef INET6
363ba650c0ume	struct sockaddr_in6 *sa6;
364ba650c0ume#endif
365ba650c0ume
366ba650c0ume	memset(&lsa, 0, sizeof(lsa));
367ba650c0ume	memset(&fsa, 0, sizeof(fsa));
3683a5c9aaglebius	if (vflag & INP_IPV4) {
369ba650c0ume		sa4 = (struct sockaddr_in *)&lsa;
3703a5c9aaglebius		sa4->sin_addr = inc->inc_laddr;
3713a5c9aaglebius		sa4->sin_port = inc->inc_lport;
372ba650c0ume		sa4->sin_family = AF_INET;
373ba650c0ume		sa4->sin_len = sizeof(struct sockaddr_in);
374ba650c0ume
375ba650c0ume		sa4 = (struct sockaddr_in *)&fsa;
3763a5c9aaglebius		sa4->sin_addr = inc->inc_faddr;
3773a5c9aaglebius		sa4->sin_port = inc->inc_fport;
378ba650c0ume		sa4->sin_family = AF_INET;
379ba650c0ume		sa4->sin_len = sizeof(struct sockaddr_in);
380ba650c0ume	}
381ba650c0ume#ifdef INET6
3823a5c9aaglebius	else if (vflag & INP_IPV6) {
383ba650c0ume		sa6 = (struct sockaddr_in6 *)&lsa;
3843a5c9aaglebius		memcpy(&sa6->sin6_addr, &inc->inc6_laddr,
385ba650c0ume		    sizeof(struct in6_addr));
3863a5c9aaglebius		sa6->sin6_port = inc->inc_lport;
387ba650c0ume		sa6->sin6_family = AF_INET6;
388ba650c0ume		sa6->sin6_len = sizeof(struct sockaddr_in6);
389ba650c0ume
390ba650c0ume		sa6 = (struct sockaddr_in6 *)&fsa;
3913a5c9aaglebius		memcpy(&sa6->sin6_addr, &inc->inc6_faddr,
392ba650c0ume		    sizeof(struct in6_addr));
3933a5c9aaglebius		sa6->sin6_port = inc->inc_fport;
394ba650c0ume		sa6->sin6_family = AF_INET6;
395ba650c0ume		sa6->sin6_len = sizeof(struct sockaddr_in6);
396ba650c0ume	}
397ba650c0ume#endif
398ead15b3ume	else
399ba650c0ume		return NULL;
400d6fecactmm
401f9ab90drgrimes	/*
402f9ab90drgrimes	 * Only take exact matches, any sockets with
403f9ab90drgrimes	 * previously unbound addresses will be deleted
404f9ab90drgrimes	 * below in the display routine because they
405f9ab90drgrimes	 * will appear as ``not seen'' in the kernel
406f9ab90drgrimes	 * data structures.
407f9ab90drgrimes	 */
4084cc97bcphk	TAILQ_FOREACH(p, &netcb, chain) {
409f9ab90drgrimes		if (!streq(proto, p->ni_proto))
410f9ab90drgrimes			continue;
411ba650c0ume		if (p->ni_lsa.ss_family != lsa.ss_family ||
412ba650c0ume		    memcmp(&p->ni_lsa, &lsa, lsa.ss_len) != 0)
413f9ab90drgrimes			continue;
414ba650c0ume		if (p->ni_fsa.ss_family == fsa.ss_family &&
415ba650c0ume		    memcmp(&p->ni_fsa, &fsa, fsa.ss_len) == 0)
416f9ab90drgrimes			break;
417f9ab90drgrimes	}
4184cc97bcphk	if (p == NULL) {
419f9ab90drgrimes		if ((p = malloc(sizeof(*p))) == NULL) {
420f9ab90drgrimes			error("Out of memory");
421d6fecactmm			return NULL;
422f9ab90drgrimes		}
4234cc97bcphk		TAILQ_INSERT_HEAD(&netcb, p, chain);
424f9ab90drgrimes		p->ni_line = -1;
425ba650c0ume		memcpy(&p->ni_lsa, &lsa, lsa.ss_len);
426ba650c0ume		memcpy(&p->ni_fsa, &fsa, fsa.ss_len);
427fa1f2b9markm		p->ni_proto = strdup(proto);
428f9ab90drgrimes		p->ni_flags = NIF_LACHG|NIF_FACHG;
429f9ab90drgrimes	}
430f9ab90drgrimes	p->ni_state = state;
431f9ab90drgrimes	p->ni_seen = 1;
432d6fecactmm	return p;
433f9ab90drgrimes}
434f9ab90drgrimes
435f9ab90drgrimes/* column locations */
436f9ab90drgrimes#define	LADDR	0
437f9ab90drgrimes#define	FADDR	LADDR+23
438f9ab90drgrimes#define	PROTO	FADDR+23
439f9ab90drgrimes#define	RCVCC	PROTO+6
440f9ab90drgrimes#define	SNDCC	RCVCC+7
441f9ab90drgrimes#define	STATE	SNDCC+7
442f9ab90drgrimes
443f9ab90drgrimesvoid
4448f8cb7cdelphijlabelnetstat(void)
445f9ab90drgrimes{
446d6fecactmm	if (use_kvm && namelist[X_TCB].n_type == 0)
447f9ab90drgrimes		return;
448f9ab90drgrimes	wmove(wnd, 0, 0); wclrtobot(wnd);
449f9ab90drgrimes	mvwaddstr(wnd, 0, LADDR, "Local Address");
450f9ab90drgrimes	mvwaddstr(wnd, 0, FADDR, "Foreign Address");
451f9ab90drgrimes	mvwaddstr(wnd, 0, PROTO, "Proto");
452f9ab90drgrimes	mvwaddstr(wnd, 0, RCVCC, "Recv-Q");
453f9ab90drgrimes	mvwaddstr(wnd, 0, SNDCC, "Send-Q");
454a14d555rgrimes	mvwaddstr(wnd, 0, STATE, "(state)");
455f9ab90drgrimes}
456f9ab90drgrimes
457f9ab90drgrimesvoid
4588f8cb7cdelphijshownetstat(void)
459f9ab90drgrimes{
460fa1f2b9markm	struct netinfo *p, *q;
46123e0bb4yar	char proto[6];
46223e0bb4yar	const char *family = "";
463f9ab90drgrimes
464f9ab90drgrimes	/*
465f9ab90drgrimes	 * First, delete any connections that have gone
466f9ab90drgrimes	 * away and adjust the position of connections
467f9ab90drgrimes	 * below to reflect the deleted line.
468f9ab90drgrimes	 */
469d6fecactmm	p = TAILQ_FIRST(&netcb);
470d6fecactmm	while (p != NULL) {
471d6fecactmm		if (p->ni_line == -1 || p->ni_seen) {
472d6fecactmm			p = TAILQ_NEXT(p, chain);
473f9ab90drgrimes			continue;
474d6fecactmm		}
475f9ab90drgrimes		wmove(wnd, p->ni_line, 0); wdeleteln(wnd);
4764cc97bcphk		TAILQ_FOREACH(q, &netcb, chain)
477f9ab90drgrimes			if (q != p && q->ni_line > p->ni_line) {
478f9ab90drgrimes				q->ni_line--;
479f9ab90drgrimes				/* this shouldn't be necessary */
480f9ab90drgrimes				q->ni_flags |= NIF_LACHG|NIF_FACHG;
481f9ab90drgrimes			}
482f9ab90drgrimes		lastrow--;
483d6fecactmm		q = TAILQ_NEXT(p, chain);
4844cc97bcphk		TAILQ_REMOVE(&netcb, p, chain);
485f9ab90drgrimes		free(p);
486f9ab90drgrimes		p = q;
487f9ab90drgrimes	}
488f9ab90drgrimes	/*
489f9ab90drgrimes	 * Update existing connections and add new ones.
490f9ab90drgrimes	 */
4914cc97bcphk	TAILQ_FOREACH(p, &netcb, chain) {
492f9ab90drgrimes		if (p->ni_line == -1) {
493f9ab90drgrimes			/*
494f9ab90drgrimes			 * Add a new entry if possible.
495f9ab90drgrimes			 */
496f9ab90drgrimes			if (lastrow > YMAX(wnd))
497f9ab90drgrimes				continue;
498f9ab90drgrimes			p->ni_line = lastrow++;
499f9ab90drgrimes			p->ni_flags |= NIF_LACHG|NIF_FACHG;
500f9ab90drgrimes		}
501f9ab90drgrimes		if (p->ni_flags & NIF_LACHG) {
502f9ab90drgrimes			wmove(wnd, p->ni_line, LADDR);
503ba650c0ume			inetprint((struct sockaddr *)&p->ni_lsa, p->ni_proto);
504f9ab90drgrimes			p->ni_flags &= ~NIF_LACHG;
505f9ab90drgrimes		}
506f9ab90drgrimes		if (p->ni_flags & NIF_FACHG) {
507f9ab90drgrimes			wmove(wnd, p->ni_line, FADDR);
508ba650c0ume			inetprint((struct sockaddr *)&p->ni_fsa, p->ni_proto);
509f9ab90drgrimes			p->ni_flags &= ~NIF_FACHG;
510f9ab90drgrimes		}
511ba650c0ume#ifdef INET6
512ba650c0ume		family = (p->ni_lsa.ss_family == AF_INET) ? "4" : "6";
513ba650c0ume#endif
514ba650c0ume		snprintf(proto, sizeof(proto), "%s%s", p->ni_proto, family);
515ba650c0ume		mvwaddstr(wnd, p->ni_line, PROTO, proto);
51686b3836jdp		mvwprintw(wnd, p->ni_line, RCVCC, "%6u", p->ni_rcvcc);
51786b3836jdp		mvwprintw(wnd, p->ni_line, SNDCC, "%6u", p->ni_sndcc);
518fa1f2b9markm		if (streq(p->ni_proto, "tcp")) {
519f9ab90drgrimes			if (p->ni_state < 0 || p->ni_state >= TCP_NSTATES)
520f9ab90drgrimes				mvwprintw(wnd, p->ni_line, STATE, "%d",
521f9ab90drgrimes				    p->ni_state);
522f9ab90drgrimes			else
523f9ab90drgrimes				mvwaddstr(wnd, p->ni_line, STATE,
524f9ab90drgrimes				    tcpstates[p->ni_state]);
525fa1f2b9markm		}
526f9ab90drgrimes		wclrtoeol(wnd);
527f9ab90drgrimes	}
528f9ab90drgrimes	if (lastrow < YMAX(wnd)) {
529f9ab90drgrimes		wmove(wnd, lastrow, 0); wclrtobot(wnd);
530f9ab90drgrimes		wmove(wnd, YMAX(wnd), 0); wdeleteln(wnd);	/* XXX */
531f9ab90drgrimes	}
532f9ab90drgrimes}
533f9ab90drgrimes
534f9ab90drgrimes/*
535f9ab90drgrimes * Pretty print an Internet address (net address + port).
536f9ab90drgrimes * If the nflag was specified, use numbers instead of names.
537f9ab90drgrimes */
538f9ab90drgrimesstatic void
5398f8cb7cdelphijinetprint(struct sockaddr *sa, const char *proto)
540f9ab90drgrimes{
541f9ab90drgrimes	struct servent *sp = 0;
542fa1f2b9markm	char line[80], *cp;
543ba650c0ume	int port;
544f9ab90drgrimes
545ba650c0ume	switch (sa->sa_family) {
546ba650c0ume	case AF_INET:
547ba650c0ume		port = ((struct sockaddr_in *)sa)->sin_port;
548ba650c0ume		break;
549ba650c0ume#ifdef INET6
550ba650c0ume	case AF_INET6:
551ba650c0ume		port = ((struct sockaddr_in6 *)sa)->sin6_port;
552ba650c0ume		break;
553ba650c0ume#endif
554ba650c0ume	default:
555ba650c0ume		port = 0;
556ba650c0ume		break;
557ba650c0ume	}
558ba650c0ume	snprintf(line, sizeof(line), "%.*s.", 16, inetname(sa));
559e7e5b53ed	cp = strchr(line, '\0');
560f9ab90drgrimes	if (!nflag && port)
561f9ab90drgrimes		sp = getservbyport(port, proto);
562f9ab90drgrimes	if (sp || port == 0)
563c4e6401bde		snprintf(cp, sizeof(line) - (cp - line), "%.8s",
564a51cfacimp		    sp ? sp->s_name : "*");
565f9ab90drgrimes	else
566c4e6401bde		snprintf(cp, sizeof(line) - (cp - line), "%d",
567a51cfacimp		    ntohs((u_short)port));
568f9ab90drgrimes	/* pad to full column to clear any garbage */
569e7e5b53ed	cp = strchr(line, '\0');
570f9ab90drgrimes	while (cp - line < 22)
571f9ab90drgrimes		*cp++ = ' ';
572ccfee52phk	line[22] = '\0';
573f9ab90drgrimes	waddstr(wnd, line);
574f9ab90drgrimes}
575f9ab90drgrimes
576f9ab90drgrimes/*
577f9ab90drgrimes * Construct an Internet address representation.
578a14d555rgrimes * If the nflag has been supplied, give
579f9ab90drgrimes * numeric value, otherwise try for symbolic name.
580f9ab90drgrimes */
581f9ab90drgrimesstatic char *
5828f8cb7cdelphijinetname(struct sockaddr *sa)
583f9ab90drgrimes{
584f9ab90drgrimes	char *cp = 0;
585ba650c0ume	static char line[NI_MAXHOST];
586f9ab90drgrimes	struct hostent *hp;
587f9ab90drgrimes	struct netent *np;
588ba650c0ume	struct in_addr in;
589ba650c0ume
590ba650c0ume#ifdef INET6
591ba650c0ume	if (sa->sa_family == AF_INET6) {
592ba650c0ume		if (memcmp(&((struct sockaddr_in6 *)sa)->sin6_addr,
593ba650c0ume		    &in6addr_any, sizeof(in6addr_any)) == 0)
594ba650c0ume			strcpy(line, "*");
595ba650c0ume		else
596ba650c0ume			getnameinfo(sa, sa->sa_len, line, sizeof(line), NULL, 0,
597ba650c0ume			    nflag ? NI_NUMERICHOST : 0);
598ba650c0ume		return (line);
599ba650c0ume	}
600ba650c0ume#endif
601f9ab90drgrimes
602ba650c0ume	in = ((struct sockaddr_in *)sa)->sin_addr;
603f9ab90drgrimes	if (!nflag && in.s_addr != INADDR_ANY) {
604f9ab90drgrimes		int net = inet_netof(in);
605f9ab90drgrimes		int lna = inet_lnaof(in);
606f9ab90drgrimes
607f9ab90drgrimes		if (lna == INADDR_ANY) {
608f9ab90drgrimes			np = getnetbyaddr(net, AF_INET);
609f9ab90drgrimes			if (np)
610f9ab90drgrimes				cp = np->n_name;
611f9ab90drgrimes		}
612b9140f0araujo		if (cp == NULL) {
613f9ab90drgrimes			hp = gethostbyaddr((char *)&in, sizeof (in), AF_INET);
614f9ab90drgrimes			if (hp)
615f9ab90drgrimes				cp = hp->h_name;
616f9ab90drgrimes		}
617f9ab90drgrimes	}
618f9ab90drgrimes	if (in.s_addr == INADDR_ANY)
619f9ab90drgrimes		strcpy(line, "*");
620f9ab90drgrimes	else if (cp)
621a51cfacimp		snprintf(line, sizeof(line), "%s", cp);
622f9ab90drgrimes	else {
623f9ab90drgrimes		in.s_addr = ntohl(in.s_addr);
624f9ab90drgrimes#define C(x)	((x) & 0xff)
625a51cfacimp		snprintf(line, sizeof(line), "%u.%u.%u.%u", C(in.s_addr >> 24),
626f9ab90drgrimes			C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr));
627f9ab90drgrimes	}
628f9ab90drgrimes	return (line);
629f9ab90drgrimes}
630f9ab90drgrimes
631f9ab90drgrimesint
6328f8cb7cdelphijcmdnetstat(const char *cmd, const char *args)
633f9ab90drgrimes{
634f9ab90drgrimes	if (prefix(cmd, "all")) {
635f9ab90drgrimes		aflag = !aflag;
636f9ab90drgrimes		goto fixup;
637f9ab90drgrimes	}
638f9ab90drgrimes	if  (prefix(cmd, "numbers") || prefix(cmd, "names")) {
6394cc97bcphk		struct netinfo *p;
640f9ab90drgrimes		int new;
641f9ab90drgrimes
642f9ab90drgrimes		new = prefix(cmd, "numbers");
643f9ab90drgrimes		if (new == nflag)
644f9ab90drgrimes			return (1);
6454cc97bcphk		TAILQ_FOREACH(p, &netcb, chain) {
646f9ab90drgrimes			if (p->ni_line == -1)
647f9ab90drgrimes				continue;
648f9ab90drgrimes			p->ni_flags |= NIF_LACHG|NIF_FACHG;
649f9ab90drgrimes		}
650f9ab90drgrimes		nflag = new;
651f9ab90drgrimes		goto redisplay;
652f9ab90drgrimes	}
653f9ab90drgrimes	if (!netcmd(cmd, args))
654f9ab90drgrimes		return (0);
655f9ab90drgrimesfixup:
656f9ab90drgrimes	fetchnetstat();
657f9ab90drgrimesredisplay:
658f9ab90drgrimes	shownetstat();
659f9ab90drgrimes	refresh();
660f9ab90drgrimes	return (1);
661f9ab90drgrimes}
662