11590Srgrimes/*-
21590Srgrimes * Copyright (c) 1980, 1992, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 4. Neither the name of the University nor the names of its contributors
141590Srgrimes *    may be used to endorse or promote products derived from this software
151590Srgrimes *    without specific prior written permission.
161590Srgrimes *
171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271590Srgrimes * SUCH DAMAGE.
281590Srgrimes */
291590Srgrimes
3087715Smarkm#include <sys/cdefs.h>
311590Srgrimes
3287715Smarkm__FBSDID("$FreeBSD$");
3387715Smarkm
3487715Smarkm#ifdef lint
3587715Smarkmstatic const char sccsid[] = "@(#)netstat.c	8.1 (Berkeley) 6/6/93";
3687715Smarkm#endif
3787715Smarkm
381590Srgrimes/*
391590Srgrimes * netstat
401590Srgrimes */
411590Srgrimes#include <sys/param.h>
4214543Sdg#include <sys/queue.h>
431590Srgrimes#include <sys/socket.h>
441590Srgrimes#include <sys/socketvar.h>
451590Srgrimes#include <sys/protosw.h>
461590Srgrimes
471590Srgrimes#include <netinet/in.h>
4836916Speter#include <arpa/inet.h>
491590Srgrimes#include <net/route.h>
501590Srgrimes#include <netinet/in_systm.h>
511590Srgrimes#include <netinet/ip.h>
52118318Sdwmalone#ifdef INET6
53118318Sdwmalone#include <netinet/ip6.h>
54118318Sdwmalone#endif
551590Srgrimes#include <netinet/in_pcb.h>
561590Srgrimes#include <netinet/ip_icmp.h>
571590Srgrimes#include <netinet/icmp_var.h>
581590Srgrimes#include <netinet/ip_var.h>
591590Srgrimes#include <netinet/tcp.h>
601590Srgrimes#include <netinet/tcpip.h>
611590Srgrimes#include <netinet/tcp_seq.h>
6274671Stmm#include <netinet/tcp_var.h>
631590Srgrimes#define TCPSTATES
641590Srgrimes#include <netinet/tcp_fsm.h>
651590Srgrimes#include <netinet/tcp_timer.h>
661590Srgrimes#include <netinet/tcp_debug.h>
671590Srgrimes#include <netinet/udp.h>
681590Srgrimes#include <netinet/udp_var.h>
691590Srgrimes
701590Srgrimes#include <netdb.h>
7187715Smarkm#include <nlist.h>
72200462Sdelphij#include <paths.h>
731590Srgrimes#include <stdlib.h>
741590Srgrimes#include <string.h>
7587715Smarkm
761590Srgrimes#include "systat.h"
771590Srgrimes#include "extern.h"
781590Srgrimes
7992922Simpstatic struct netinfo *enter(struct inpcb *, int, const char *);
8092922Simpstatic void enter_kvm(struct inpcb *, struct socket *, int, const char *);
8192922Simpstatic void enter_sysctl(struct inpcb *, struct xsocket *, int, const char *);
8292922Simpstatic void fetchnetstat_kvm(void);
8392922Simpstatic void fetchnetstat_sysctl(void);
84142093Sumestatic char *inetname(struct sockaddr *);
85142093Sumestatic void inetprint(struct sockaddr *, const char *);
861590Srgrimes
871590Srgrimes#define	streq(a,b)	(strcmp(a,b)==0)
8850635Speter#define	YMAX(w)		((w)->_maxy-1)
891590Srgrimes
901590SrgrimesWINDOW *
91175387Sdelphijopennetstat(void)
921590Srgrimes{
931590Srgrimes	sethostent(1);
941590Srgrimes	setnetent(1);
95158160Sbde	return (subwin(stdscr, LINES-3-1, 0, MAINWIN_ROW, 0));
961590Srgrimes}
971590Srgrimes
981590Srgrimesstruct netinfo {
9970523Sphk	TAILQ_ENTRY(netinfo) chain;
1001590Srgrimes	short	ni_line;		/* line on screen */
1011590Srgrimes	short	ni_seen;		/* 0 when not present in list */
1021590Srgrimes	short	ni_flags;
1031590Srgrimes#define	NIF_LACHG	0x1		/* local address changed */
1041590Srgrimes#define	NIF_FACHG	0x2		/* foreign address changed */
1051590Srgrimes	short	ni_state;		/* tcp state */
10687715Smarkm	const char	*ni_proto;		/* protocol */
107142093Sume	struct sockaddr_storage ni_lsa;	/* local address */
108142093Sume	struct sockaddr_storage	ni_fsa;	/* foreign address */
109100591Sjdp	u_int	ni_rcvcc;		/* rcv buffer character count */
110100591Sjdp	u_int	ni_sndcc;		/* snd buffer character count */
1111590Srgrimes};
1121590Srgrimes
11370523SphkTAILQ_HEAD(netinfohead, netinfo) netcb = TAILQ_HEAD_INITIALIZER(netcb);
1141590Srgrimes
1151590Srgrimesstatic	int aflag = 0;
1161590Srgrimesstatic	int nflag = 0;
1171590Srgrimesstatic	int lastrow = 1;
1181590Srgrimes
1191590Srgrimesvoid
120175387Sdelphijclosenetstat(WINDOW *w)
1211590Srgrimes{
12287715Smarkm	struct netinfo *p;
1231590Srgrimes
1241590Srgrimes	endhostent();
1251590Srgrimes	endnetent();
12670526Sphk	TAILQ_FOREACH(p, &netcb, chain) {
1271590Srgrimes		if (p->ni_line != -1)
1281590Srgrimes			lastrow--;
1291590Srgrimes		p->ni_line = -1;
1301590Srgrimes	}
131231279Sed	if (w != NULL) {
1321590Srgrimes		wclear(w);
1331590Srgrimes		wrefresh(w);
1341590Srgrimes		delwin(w);
1351590Srgrimes	}
1361590Srgrimes}
1371590Srgrimes
13887715Smarkmstatic const char *miblist[] = {
13974671Stmm	"net.inet.tcp.pcblist",
140158161Sbde	"net.inet.udp.pcblist"
14174671Stmm};
14274671Stmm
143164679Syarstatic char tcb[] = "tcb", udb[] = "udb";
144164679Syar
14574671Stmmstruct nlist namelist[] = {
1461590Srgrimes#define	X_TCB	0
147164679Syar	{ .n_name = tcb },
1481590Srgrimes#define	X_UDB	1
149164679Syar	{ .n_name = udb },
150164679Syar	{ .n_name = NULL },
1511590Srgrimes};
1521590Srgrimes
1531590Srgrimesint
154175387Sdelphijinitnetstat(void)
1551590Srgrimes{
1561590Srgrimes	protos = TCP|UDP;
1571590Srgrimes	return(1);
1581590Srgrimes}
1591590Srgrimes
1601590Srgrimesvoid
161175387Sdelphijfetchnetstat(void)
1621590Srgrimes{
16374671Stmm	if (use_kvm)
16474671Stmm		fetchnetstat_kvm();
16574671Stmm	else
16674671Stmm		fetchnetstat_sysctl();
16774671Stmm}
16874671Stmm
16974671Stmmstatic void
170175387Sdelphijfetchnetstat_kvm(void)
17174671Stmm{
17287715Smarkm	struct inpcb *next;
17387715Smarkm	struct netinfo *p;
1747715Sdg	struct inpcbhead head;
1751590Srgrimes	struct inpcb inpcb;
1761590Srgrimes	struct socket sockb;
1771590Srgrimes	struct tcpcb tcpcb;
1781590Srgrimes	void *off;
1791590Srgrimes	int istcp;
1801590Srgrimes
1811590Srgrimes	if (namelist[X_TCB].n_value == 0)
1821590Srgrimes		return;
18370523Sphk	TAILQ_FOREACH(p, &netcb, chain)
1841590Srgrimes		p->ni_seen = 0;
1851590Srgrimes	if (protos&TCP) {
1868874Srgrimes		off = NPTR(X_TCB);
1871590Srgrimes		istcp = 1;
1881590Srgrimes	}
1891590Srgrimes	else if (protos&UDP) {
1908874Srgrimes		off = NPTR(X_UDB);
1911590Srgrimes		istcp = 0;
1921590Srgrimes	}
1931590Srgrimes	else {
1941590Srgrimes		error("No protocols to display");
1951590Srgrimes		return;
1961590Srgrimes	}
1971590Srgrimesagain:
1987715Sdg	KREAD(off, &head, sizeof (struct inpcbhead));
19970523Sphk	LIST_FOREACH(next, &head, inp_list) {
2001590Srgrimes		KREAD(next, &inpcb, sizeof (inpcb));
20170526Sphk		next = &inpcb;
202142093Sume		if (!aflag) {
203142093Sume			if (inpcb.inp_vflag & INP_IPV4) {
204142093Sume				if (inet_lnaof(inpcb.inp_laddr) == INADDR_ANY)
205142093Sume					continue;
206142093Sume			}
207142093Sume#ifdef INET6
208142093Sume			else if (inpcb.inp_vflag & INP_IPV6) {
209142093Sume				if (memcmp(&inpcb.in6p_laddr,
210142093Sume				    &in6addr_any, sizeof(in6addr_any)) == 0)
211142093Sume					continue;
212142093Sume			}
213142093Sume#endif
214142093Sume		}
2151590Srgrimes		if (nhosts && !checkhost(&inpcb))
2161590Srgrimes			continue;
2171590Srgrimes		if (nports && !checkport(&inpcb))
2181590Srgrimes			continue;
2191590Srgrimes		if (istcp) {
220189848Srwatson			if (inpcb.inp_flags & INP_TIMEWAIT) {
221123800Ssilby				bzero(&sockb, sizeof(sockb));
222123800Ssilby				enter_kvm(&inpcb, &sockb, TCPS_TIME_WAIT,
223123800Ssilby					 "tcp");
224123800Ssilby			} else {
225123800Ssilby				KREAD(inpcb.inp_socket, &sockb,
226123800Ssilby					sizeof (sockb));
227123800Ssilby				KREAD(inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb));
228123800Ssilby				enter_kvm(&inpcb, &sockb, tcpcb.t_state,
229123800Ssilby					"tcp");
230123800Ssilby			}
2311590Srgrimes		} else
23274671Stmm			enter_kvm(&inpcb, &sockb, 0, "udp");
2331590Srgrimes	}
2341590Srgrimes	if (istcp && (protos&UDP)) {
2351590Srgrimes		istcp = 0;
2361590Srgrimes		off = NPTR(X_UDB);
2371590Srgrimes		goto again;
2381590Srgrimes	}
2391590Srgrimes}
2401590Srgrimes
2411590Srgrimesstatic void
242175387Sdelphijfetchnetstat_sysctl(void)
24374671Stmm{
24487715Smarkm	struct netinfo *p;
24574671Stmm	int idx;
24674671Stmm	struct xinpgen *inpg;
24774671Stmm	char *cur, *end;
24874671Stmm	struct inpcb *inpcb;
249164680Syar	struct xinpcb *xip = NULL;
250164680Syar	struct xtcpcb *xtp = NULL;
25174671Stmm	int plen;
25274671Stmm	size_t lsz;
25374671Stmm
25474671Stmm	TAILQ_FOREACH(p, &netcb, chain)
25574671Stmm		p->ni_seen = 0;
25674671Stmm	if (protos&TCP) {
25774671Stmm		idx = 0;
25874671Stmm	} else if (protos&UDP) {
25974671Stmm		idx = 1;
26074671Stmm	} else {
26174671Stmm		error("No protocols to display");
26274671Stmm		return;
26374671Stmm	}
26474671Stmm
26574671Stmm	for (;idx < 2; idx++) {
26674671Stmm		if (idx == 1 && !(protos&UDP))
26774671Stmm			break;
26874671Stmm		inpg = (struct xinpgen *)sysctl_dynread(miblist[idx], &lsz);
26974671Stmm		if (inpg == NULL) {
27074671Stmm			error("sysctl(%s...) failed", miblist[idx]);
27174671Stmm			continue;
27274671Stmm		}
273158161Sbde		/*
27474671Stmm		 * We currently do no require a consistent pcb list.
275158161Sbde		 * Try to be robust in case of struct size changes
27674671Stmm		 */
27774671Stmm		cur = ((char *)inpg) + inpg->xig_len;
27874671Stmm		/* There is also a trailing struct xinpgen */
27974671Stmm		end = ((char *)inpg) + lsz - inpg->xig_len;
28074671Stmm		if (end <= cur) {
28174671Stmm			free(inpg);
28274671Stmm			continue;
28374671Stmm		}
28474671Stmm		if (idx == 0) { /* TCP */
28574671Stmm			xtp = (struct xtcpcb *)cur;
28674671Stmm			plen = xtp->xt_len;
28774671Stmm		} else {
28874671Stmm			xip = (struct xinpcb *)cur;
28974671Stmm			plen = xip->xi_len;
29074671Stmm		}
29174671Stmm		while (cur + plen <= end) {
29274671Stmm			if (idx == 0) { /* TCP */
29374671Stmm				xtp = (struct xtcpcb *)cur;
29474671Stmm				inpcb = &xtp->xt_inp;
29574671Stmm			} else {
29674671Stmm				xip = (struct xinpcb *)cur;
29774671Stmm				inpcb = &xip->xi_inp;
29874671Stmm			}
29974671Stmm			cur += plen;
30074671Stmm
301142093Sume			if (!aflag) {
302142093Sume				if (inpcb->inp_vflag & INP_IPV4) {
303142093Sume					if (inet_lnaof(inpcb->inp_laddr) ==
304142093Sume					    INADDR_ANY)
305142093Sume						continue;
306142093Sume				}
307142093Sume#ifdef INET6
308142093Sume				else if (inpcb->inp_vflag & INP_IPV6) {
309142093Sume					if (memcmp(&inpcb->in6p_laddr,
310142093Sume					    &in6addr_any, sizeof(in6addr_any))
311142093Sume					    == 0)
312142093Sume						continue;
313142093Sume				}
314142093Sume#endif
315142093Sume			}
31674671Stmm			if (nhosts && !checkhost(inpcb))
31774671Stmm				continue;
31874671Stmm			if (nports && !checkport(inpcb))
31974671Stmm				continue;
32074671Stmm			if (idx == 0)	/* TCP */
321158161Sbde				enter_sysctl(inpcb, &xtp->xt_socket,
32274671Stmm				    xtp->xt_tp.t_state, "tcp");
32374671Stmm			else		/* UDP */
32474671Stmm				enter_sysctl(inpcb, &xip->xi_socket, 0, "udp");
32574671Stmm		}
32674671Stmm		free(inpg);
32774671Stmm	}
32874671Stmm}
32974671Stmm
33074671Stmmstatic void
331175387Sdelphijenter_kvm(struct inpcb *inp, struct socket *so, int state, const char *proto)
3321590Srgrimes{
33387715Smarkm	struct netinfo *p;
3341590Srgrimes
33574671Stmm	if ((p = enter(inp, state, proto)) != NULL) {
33674671Stmm		p->ni_rcvcc = so->so_rcv.sb_cc;
33774671Stmm		p->ni_sndcc = so->so_snd.sb_cc;
33874671Stmm	}
33974671Stmm}
34074671Stmm
34174671Stmmstatic void
342175387Sdelphijenter_sysctl(struct inpcb *inp, struct xsocket *so, int state, const char *proto)
34374671Stmm{
34487715Smarkm	struct netinfo *p;
34574671Stmm
34674671Stmm	if ((p = enter(inp, state, proto)) != NULL) {
34774671Stmm		p->ni_rcvcc = so->so_rcv.sb_cc;
34874671Stmm		p->ni_sndcc = so->so_snd.sb_cc;
34974671Stmm	}
35074671Stmm}
35174671Stmm
35274671Stmm
35374671Stmmstatic struct netinfo *
354175387Sdelphijenter(struct inpcb *inp, int state, const char *proto)
35574671Stmm{
35687715Smarkm	struct netinfo *p;
357142093Sume	struct sockaddr_storage lsa, fsa;
358142093Sume	struct sockaddr_in *sa4;
359142093Sume#ifdef INET6
360142093Sume	struct sockaddr_in6 *sa6;
361142093Sume#endif
36274671Stmm
363142093Sume	memset(&lsa, 0, sizeof(lsa));
364142093Sume	memset(&fsa, 0, sizeof(fsa));
365142093Sume	if (inp->inp_vflag & INP_IPV4) {
366142093Sume		sa4 = (struct sockaddr_in *)&lsa;
367142093Sume		sa4->sin_addr = inp->inp_laddr;
368142093Sume		sa4->sin_port = inp->inp_lport;
369142093Sume		sa4->sin_family = AF_INET;
370142093Sume		sa4->sin_len = sizeof(struct sockaddr_in);
371142093Sume
372142093Sume		sa4 = (struct sockaddr_in *)&fsa;
373142093Sume		sa4->sin_addr = inp->inp_faddr;
374142093Sume		sa4->sin_port = inp->inp_fport;
375142093Sume		sa4->sin_family = AF_INET;
376142093Sume		sa4->sin_len = sizeof(struct sockaddr_in);
377142093Sume	}
378142093Sume#ifdef INET6
379142093Sume	else if (inp->inp_vflag & INP_IPV6) {
380142093Sume		sa6 = (struct sockaddr_in6 *)&lsa;
381142093Sume		memcpy(&sa6->sin6_addr, &inp->in6p_laddr,
382142093Sume		    sizeof(struct in6_addr));
383142093Sume		sa6->sin6_port = inp->inp_lport;
384142093Sume		sa6->sin6_family = AF_INET6;
385142093Sume		sa6->sin6_len = sizeof(struct sockaddr_in6);
386142093Sume
387142093Sume		sa6 = (struct sockaddr_in6 *)&fsa;
388142093Sume		memcpy(&sa6->sin6_addr, &inp->in6p_faddr,
389142093Sume		    sizeof(struct in6_addr));
390142093Sume		sa6->sin6_port = inp->inp_fport;
391142093Sume		sa6->sin6_family = AF_INET6;
392142093Sume		sa6->sin6_len = sizeof(struct sockaddr_in6);
393142093Sume	}
394142093Sume#endif
395142095Sume	else
396142093Sume		return NULL;
397142093Sume
3981590Srgrimes	/*
3991590Srgrimes	 * Only take exact matches, any sockets with
4001590Srgrimes	 * previously unbound addresses will be deleted
4011590Srgrimes	 * below in the display routine because they
4021590Srgrimes	 * will appear as ``not seen'' in the kernel
4031590Srgrimes	 * data structures.
4041590Srgrimes	 */
40570523Sphk	TAILQ_FOREACH(p, &netcb, chain) {
4061590Srgrimes		if (!streq(proto, p->ni_proto))
4071590Srgrimes			continue;
408142093Sume		if (p->ni_lsa.ss_family != lsa.ss_family ||
409142093Sume		    memcmp(&p->ni_lsa, &lsa, lsa.ss_len) != 0)
4101590Srgrimes			continue;
411142093Sume		if (p->ni_fsa.ss_family == fsa.ss_family &&
412142093Sume		    memcmp(&p->ni_fsa, &fsa, fsa.ss_len) == 0)
4131590Srgrimes			break;
4141590Srgrimes	}
41570523Sphk	if (p == NULL) {
4161590Srgrimes		if ((p = malloc(sizeof(*p))) == NULL) {
4171590Srgrimes			error("Out of memory");
41874671Stmm			return NULL;
4191590Srgrimes		}
42070523Sphk		TAILQ_INSERT_HEAD(&netcb, p, chain);
4211590Srgrimes		p->ni_line = -1;
422142093Sume		memcpy(&p->ni_lsa, &lsa, lsa.ss_len);
423142093Sume		memcpy(&p->ni_fsa, &fsa, fsa.ss_len);
42487715Smarkm		p->ni_proto = strdup(proto);
4251590Srgrimes		p->ni_flags = NIF_LACHG|NIF_FACHG;
4261590Srgrimes	}
4271590Srgrimes	p->ni_state = state;
4281590Srgrimes	p->ni_seen = 1;
42974671Stmm	return p;
4301590Srgrimes}
4311590Srgrimes
4321590Srgrimes/* column locations */
4331590Srgrimes#define	LADDR	0
4341590Srgrimes#define	FADDR	LADDR+23
4351590Srgrimes#define	PROTO	FADDR+23
4361590Srgrimes#define	RCVCC	PROTO+6
4371590Srgrimes#define	SNDCC	RCVCC+7
4381590Srgrimes#define	STATE	SNDCC+7
4391590Srgrimes
4401590Srgrimes
4411590Srgrimesvoid
442175387Sdelphijlabelnetstat(void)
4431590Srgrimes{
44474671Stmm	if (use_kvm && namelist[X_TCB].n_type == 0)
4451590Srgrimes		return;
4461590Srgrimes	wmove(wnd, 0, 0); wclrtobot(wnd);
4471590Srgrimes	mvwaddstr(wnd, 0, LADDR, "Local Address");
4481590Srgrimes	mvwaddstr(wnd, 0, FADDR, "Foreign Address");
4491590Srgrimes	mvwaddstr(wnd, 0, PROTO, "Proto");
4501590Srgrimes	mvwaddstr(wnd, 0, RCVCC, "Recv-Q");
4511590Srgrimes	mvwaddstr(wnd, 0, SNDCC, "Send-Q");
4528874Srgrimes	mvwaddstr(wnd, 0, STATE, "(state)");
4531590Srgrimes}
4541590Srgrimes
4551590Srgrimesvoid
456175387Sdelphijshownetstat(void)
4571590Srgrimes{
45887715Smarkm	struct netinfo *p, *q;
459164678Syar	char proto[6];
460164678Syar	const char *family = "";
4611590Srgrimes
4621590Srgrimes	/*
4631590Srgrimes	 * First, delete any connections that have gone
4641590Srgrimes	 * away and adjust the position of connections
4651590Srgrimes	 * below to reflect the deleted line.
4661590Srgrimes	 */
46774671Stmm	p = TAILQ_FIRST(&netcb);
46874671Stmm	while (p != NULL) {
46974671Stmm		if (p->ni_line == -1 || p->ni_seen) {
47074671Stmm			p = TAILQ_NEXT(p, chain);
4711590Srgrimes			continue;
47274671Stmm		}
4731590Srgrimes		wmove(wnd, p->ni_line, 0); wdeleteln(wnd);
47470523Sphk		TAILQ_FOREACH(q, &netcb, chain)
4751590Srgrimes			if (q != p && q->ni_line > p->ni_line) {
4761590Srgrimes				q->ni_line--;
4771590Srgrimes				/* this shouldn't be necessary */
4781590Srgrimes				q->ni_flags |= NIF_LACHG|NIF_FACHG;
4791590Srgrimes			}
4801590Srgrimes		lastrow--;
48174671Stmm		q = TAILQ_NEXT(p, chain);
48270523Sphk		TAILQ_REMOVE(&netcb, p, chain);
4831590Srgrimes		free(p);
4841590Srgrimes		p = q;
4851590Srgrimes	}
4861590Srgrimes	/*
4871590Srgrimes	 * Update existing connections and add new ones.
4881590Srgrimes	 */
48970523Sphk	TAILQ_FOREACH(p, &netcb, chain) {
4901590Srgrimes		if (p->ni_line == -1) {
4911590Srgrimes			/*
4921590Srgrimes			 * Add a new entry if possible.
4931590Srgrimes			 */
4941590Srgrimes			if (lastrow > YMAX(wnd))
4951590Srgrimes				continue;
4961590Srgrimes			p->ni_line = lastrow++;
4971590Srgrimes			p->ni_flags |= NIF_LACHG|NIF_FACHG;
4981590Srgrimes		}
4991590Srgrimes		if (p->ni_flags & NIF_LACHG) {
5001590Srgrimes			wmove(wnd, p->ni_line, LADDR);
501142093Sume			inetprint((struct sockaddr *)&p->ni_lsa, p->ni_proto);
5021590Srgrimes			p->ni_flags &= ~NIF_LACHG;
5031590Srgrimes		}
5041590Srgrimes		if (p->ni_flags & NIF_FACHG) {
5051590Srgrimes			wmove(wnd, p->ni_line, FADDR);
506142093Sume			inetprint((struct sockaddr *)&p->ni_fsa, p->ni_proto);
5071590Srgrimes			p->ni_flags &= ~NIF_FACHG;
5081590Srgrimes		}
509142093Sume#ifdef INET6
510142093Sume		family = (p->ni_lsa.ss_family == AF_INET) ? "4" : "6";
511142093Sume#endif
512142093Sume		snprintf(proto, sizeof(proto), "%s%s", p->ni_proto, family);
513142093Sume		mvwaddstr(wnd, p->ni_line, PROTO, proto);
514100591Sjdp		mvwprintw(wnd, p->ni_line, RCVCC, "%6u", p->ni_rcvcc);
515100591Sjdp		mvwprintw(wnd, p->ni_line, SNDCC, "%6u", p->ni_sndcc);
51687715Smarkm		if (streq(p->ni_proto, "tcp")) {
5171590Srgrimes			if (p->ni_state < 0 || p->ni_state >= TCP_NSTATES)
5181590Srgrimes				mvwprintw(wnd, p->ni_line, STATE, "%d",
5191590Srgrimes				    p->ni_state);
5201590Srgrimes			else
5211590Srgrimes				mvwaddstr(wnd, p->ni_line, STATE,
5221590Srgrimes				    tcpstates[p->ni_state]);
52387715Smarkm		}
5241590Srgrimes		wclrtoeol(wnd);
5251590Srgrimes	}
5261590Srgrimes	if (lastrow < YMAX(wnd)) {
5271590Srgrimes		wmove(wnd, lastrow, 0); wclrtobot(wnd);
5281590Srgrimes		wmove(wnd, YMAX(wnd), 0); wdeleteln(wnd);	/* XXX */
5291590Srgrimes	}
5301590Srgrimes}
5311590Srgrimes
5321590Srgrimes/*
5331590Srgrimes * Pretty print an Internet address (net address + port).
5341590Srgrimes * If the nflag was specified, use numbers instead of names.
5351590Srgrimes */
5361590Srgrimesstatic void
537175387Sdelphijinetprint(struct sockaddr *sa, const char *proto)
5381590Srgrimes{
5391590Srgrimes	struct servent *sp = 0;
54087715Smarkm	char line[80], *cp;
541142093Sume	int port;
5421590Srgrimes
543142093Sume	switch (sa->sa_family) {
544142093Sume	case AF_INET:
545142093Sume		port = ((struct sockaddr_in *)sa)->sin_port;
546142093Sume		break;
547142093Sume#ifdef INET6
548142093Sume	case AF_INET6:
549142093Sume		port = ((struct sockaddr_in6 *)sa)->sin6_port;
550142093Sume		break;
551142093Sume#endif
552142093Sume	default:
553142093Sume		port = 0;
554142093Sume		break;
555142093Sume	}
556142093Sume	snprintf(line, sizeof(line), "%.*s.", 16, inetname(sa));
5571590Srgrimes	cp = index(line, '\0');
5581590Srgrimes	if (!nflag && port)
5591590Srgrimes		sp = getservbyport(port, proto);
5601590Srgrimes	if (sp || port == 0)
561158161Sbde		snprintf(cp, sizeof(line) - (cp - line), "%.8s",
56236789Simp		    sp ? sp->s_name : "*");
5631590Srgrimes	else
564158161Sbde		snprintf(cp, sizeof(line) - (cp - line), "%d",
56536789Simp		    ntohs((u_short)port));
5661590Srgrimes	/* pad to full column to clear any garbage */
5671590Srgrimes	cp = index(line, '\0');
5681590Srgrimes	while (cp - line < 22)
5691590Srgrimes		*cp++ = ' ';
57019330Sphk	line[22] = '\0';
5711590Srgrimes	waddstr(wnd, line);
5721590Srgrimes}
5731590Srgrimes
5741590Srgrimes/*
5751590Srgrimes * Construct an Internet address representation.
5768874Srgrimes * If the nflag has been supplied, give
5771590Srgrimes * numeric value, otherwise try for symbolic name.
5781590Srgrimes */
5791590Srgrimesstatic char *
580175387Sdelphijinetname(struct sockaddr *sa)
5811590Srgrimes{
5821590Srgrimes	char *cp = 0;
583142093Sume	static char line[NI_MAXHOST];
5841590Srgrimes	struct hostent *hp;
5851590Srgrimes	struct netent *np;
586142093Sume	struct in_addr in;
5871590Srgrimes
588142093Sume#ifdef INET6
589142093Sume	if (sa->sa_family == AF_INET6) {
590142093Sume		if (memcmp(&((struct sockaddr_in6 *)sa)->sin6_addr,
591142093Sume		    &in6addr_any, sizeof(in6addr_any)) == 0)
592142093Sume			strcpy(line, "*");
593142093Sume		else
594142093Sume			getnameinfo(sa, sa->sa_len, line, sizeof(line), NULL, 0,
595142093Sume			    nflag ? NI_NUMERICHOST : 0);
596142093Sume		return (line);
597142093Sume	}
598142093Sume#endif
599142093Sume
600142093Sume	in = ((struct sockaddr_in *)sa)->sin_addr;
6011590Srgrimes	if (!nflag && in.s_addr != INADDR_ANY) {
6021590Srgrimes		int net = inet_netof(in);
6031590Srgrimes		int lna = inet_lnaof(in);
6041590Srgrimes
6051590Srgrimes		if (lna == INADDR_ANY) {
6061590Srgrimes			np = getnetbyaddr(net, AF_INET);
6071590Srgrimes			if (np)
6081590Srgrimes				cp = np->n_name;
6091590Srgrimes		}
6101590Srgrimes		if (cp == 0) {
6111590Srgrimes			hp = gethostbyaddr((char *)&in, sizeof (in), AF_INET);
6121590Srgrimes			if (hp)
6131590Srgrimes				cp = hp->h_name;
6141590Srgrimes		}
6151590Srgrimes	}
6161590Srgrimes	if (in.s_addr == INADDR_ANY)
6171590Srgrimes		strcpy(line, "*");
6181590Srgrimes	else if (cp)
61936789Simp		snprintf(line, sizeof(line), "%s", cp);
6201590Srgrimes	else {
6211590Srgrimes		in.s_addr = ntohl(in.s_addr);
6221590Srgrimes#define C(x)	((x) & 0xff)
62336789Simp		snprintf(line, sizeof(line), "%u.%u.%u.%u", C(in.s_addr >> 24),
6241590Srgrimes			C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr));
6251590Srgrimes	}
6261590Srgrimes	return (line);
6271590Srgrimes}
6281590Srgrimes
6291590Srgrimesint
630175387Sdelphijcmdnetstat(const char *cmd, const char *args)
6311590Srgrimes{
6321590Srgrimes	if (prefix(cmd, "all")) {
6331590Srgrimes		aflag = !aflag;
6341590Srgrimes		goto fixup;
6351590Srgrimes	}
6361590Srgrimes	if  (prefix(cmd, "numbers") || prefix(cmd, "names")) {
63770523Sphk		struct netinfo *p;
6381590Srgrimes		int new;
6391590Srgrimes
6401590Srgrimes		new = prefix(cmd, "numbers");
6411590Srgrimes		if (new == nflag)
6421590Srgrimes			return (1);
64370523Sphk		TAILQ_FOREACH(p, &netcb, chain) {
6441590Srgrimes			if (p->ni_line == -1)
6451590Srgrimes				continue;
6461590Srgrimes			p->ni_flags |= NIF_LACHG|NIF_FACHG;
6471590Srgrimes		}
6481590Srgrimes		nflag = new;
6491590Srgrimes		goto redisplay;
6501590Srgrimes	}
6511590Srgrimes	if (!netcmd(cmd, args))
6521590Srgrimes		return (0);
6531590Srgrimesfixup:
6541590Srgrimes	fetchnetstat();
6551590Srgrimesredisplay:
6561590Srgrimes	shownetstat();
6571590Srgrimes	refresh();
6581590Srgrimes	return (1);
6591590Srgrimes}
660