netstat.c revision 123800
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 * 3. All advertising materials mentioning features or use of this software
141590Srgrimes *    must display the following acknowledgement:
151590Srgrimes *	This product includes software developed by the University of
161590Srgrimes *	California, Berkeley and its contributors.
171590Srgrimes * 4. Neither the name of the University nor the names of its contributors
181590Srgrimes *    may be used to endorse or promote products derived from this software
191590Srgrimes *    without specific prior written permission.
201590Srgrimes *
211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311590Srgrimes * SUCH DAMAGE.
321590Srgrimes */
331590Srgrimes
3487715Smarkm#include <sys/cdefs.h>
351590Srgrimes
3687715Smarkm__FBSDID("$FreeBSD: head/usr.bin/systat/netstat.c 123800 2003-12-24 08:54:53Z silby $");
3787715Smarkm
3887715Smarkm#ifdef lint
3987715Smarkmstatic const char sccsid[] = "@(#)netstat.c	8.1 (Berkeley) 6/6/93";
4087715Smarkm#endif
4187715Smarkm
421590Srgrimes/*
431590Srgrimes * netstat
441590Srgrimes */
451590Srgrimes#include <sys/param.h>
4614543Sdg#include <sys/queue.h>
471590Srgrimes#include <sys/socket.h>
481590Srgrimes#include <sys/socketvar.h>
491590Srgrimes#include <sys/protosw.h>
501590Srgrimes
511590Srgrimes#include <netinet/in.h>
5236916Speter#include <arpa/inet.h>
531590Srgrimes#include <net/route.h>
541590Srgrimes#include <netinet/in_systm.h>
551590Srgrimes#include <netinet/ip.h>
56118318Sdwmalone#ifdef INET6
57118318Sdwmalone#include <netinet/ip6.h>
58118318Sdwmalone#endif
591590Srgrimes#include <netinet/in_pcb.h>
601590Srgrimes#include <netinet/ip_icmp.h>
611590Srgrimes#include <netinet/icmp_var.h>
621590Srgrimes#include <netinet/ip_var.h>
631590Srgrimes#include <netinet/tcp.h>
641590Srgrimes#include <netinet/tcpip.h>
651590Srgrimes#include <netinet/tcp_seq.h>
6674671Stmm#include <netinet/tcp_var.h>
671590Srgrimes#define TCPSTATES
681590Srgrimes#include <netinet/tcp_fsm.h>
691590Srgrimes#include <netinet/tcp_timer.h>
701590Srgrimes#include <netinet/tcp_var.h>
711590Srgrimes#include <netinet/tcp_debug.h>
721590Srgrimes#include <netinet/udp.h>
731590Srgrimes#include <netinet/udp_var.h>
741590Srgrimes
751590Srgrimes#include <netdb.h>
7687715Smarkm#include <nlist.h>
7787715Smarkm#include <paths.h>
781590Srgrimes#include <stdlib.h>
791590Srgrimes#include <string.h>
8087715Smarkm
811590Srgrimes#include "systat.h"
821590Srgrimes#include "extern.h"
831590Srgrimes
8492922Simpstatic struct netinfo *enter(struct inpcb *, int, const char *);
8592922Simpstatic void enter_kvm(struct inpcb *, struct socket *, int, const char *);
8692922Simpstatic void enter_sysctl(struct inpcb *, struct xsocket *, int, const char *);
8792922Simpstatic void fetchnetstat_kvm(void);
8892922Simpstatic void fetchnetstat_sysctl(void);
8992922Simpstatic char *inetname(struct in_addr);
9092922Simpstatic void inetprint(struct in_addr *, int, const char *);
911590Srgrimes
921590Srgrimes#define	streq(a,b)	(strcmp(a,b)==0)
9350635Speter#define	YMAX(w)		((w)->_maxy-1)
941590Srgrimes
951590SrgrimesWINDOW *
961590Srgrimesopennetstat()
971590Srgrimes{
981590Srgrimes	sethostent(1);
991590Srgrimes	setnetent(1);
1001590Srgrimes	return (subwin(stdscr, LINES-5-1, 0, 5, 0));
1011590Srgrimes}
1021590Srgrimes
1031590Srgrimesstruct netinfo {
10470523Sphk	TAILQ_ENTRY(netinfo) chain;
1051590Srgrimes	short	ni_line;		/* line on screen */
1061590Srgrimes	short	ni_seen;		/* 0 when not present in list */
1071590Srgrimes	short	ni_flags;
1081590Srgrimes#define	NIF_LACHG	0x1		/* local address changed */
1091590Srgrimes#define	NIF_FACHG	0x2		/* foreign address changed */
1101590Srgrimes	short	ni_state;		/* tcp state */
11187715Smarkm	const char	*ni_proto;		/* protocol */
1121590Srgrimes	struct	in_addr ni_laddr;	/* local address */
1131590Srgrimes	long	ni_lport;		/* local port */
1141590Srgrimes	struct	in_addr	ni_faddr;	/* foreign address */
1151590Srgrimes	long	ni_fport;		/* foreign port */
116100591Sjdp	u_int	ni_rcvcc;		/* rcv buffer character count */
117100591Sjdp	u_int	ni_sndcc;		/* snd buffer character count */
1181590Srgrimes};
1191590Srgrimes
12070523SphkTAILQ_HEAD(netinfohead, netinfo) netcb = TAILQ_HEAD_INITIALIZER(netcb);
1211590Srgrimes
1221590Srgrimesstatic	int aflag = 0;
1231590Srgrimesstatic	int nflag = 0;
1241590Srgrimesstatic	int lastrow = 1;
1251590Srgrimes
1261590Srgrimesvoid
1271590Srgrimesclosenetstat(w)
1281590Srgrimes        WINDOW *w;
1291590Srgrimes{
13087715Smarkm	struct netinfo *p;
1311590Srgrimes
1321590Srgrimes	endhostent();
1331590Srgrimes	endnetent();
13470526Sphk	TAILQ_FOREACH(p, &netcb, chain) {
1351590Srgrimes		if (p->ni_line != -1)
1361590Srgrimes			lastrow--;
1371590Srgrimes		p->ni_line = -1;
1381590Srgrimes	}
1391590Srgrimes        if (w != NULL) {
1401590Srgrimes		wclear(w);
1411590Srgrimes		wrefresh(w);
1421590Srgrimes		delwin(w);
1431590Srgrimes	}
1441590Srgrimes}
1451590Srgrimes
14687715Smarkmstatic const char *miblist[] = {
14774671Stmm	"net.inet.tcp.pcblist",
14874671Stmm	"net.inet.udp.pcblist"
14974671Stmm};
15074671Stmm
15174671Stmmstruct nlist namelist[] = {
1521590Srgrimes#define	X_TCB	0
15374671Stmm	{ "tcb" },
1541590Srgrimes#define	X_UDB	1
15574671Stmm	{ "udb" },
1561590Srgrimes	{ "" },
1571590Srgrimes};
1581590Srgrimes
1591590Srgrimesint
1601590Srgrimesinitnetstat()
1611590Srgrimes{
1621590Srgrimes	protos = TCP|UDP;
1631590Srgrimes	return(1);
1641590Srgrimes}
1651590Srgrimes
1661590Srgrimesvoid
1671590Srgrimesfetchnetstat()
1681590Srgrimes{
16974671Stmm	if (use_kvm)
17074671Stmm		fetchnetstat_kvm();
17174671Stmm	else
17274671Stmm		fetchnetstat_sysctl();
17374671Stmm}
17474671Stmm
17574671Stmmstatic void
17674671Stmmfetchnetstat_kvm()
17774671Stmm{
17887715Smarkm	struct inpcb *next;
17987715Smarkm	struct netinfo *p;
1807715Sdg	struct inpcbhead head;
1811590Srgrimes	struct inpcb inpcb;
1821590Srgrimes	struct socket sockb;
1831590Srgrimes	struct tcpcb tcpcb;
1841590Srgrimes	void *off;
1851590Srgrimes	int istcp;
1861590Srgrimes
1871590Srgrimes	if (namelist[X_TCB].n_value == 0)
1881590Srgrimes		return;
18970523Sphk	TAILQ_FOREACH(p, &netcb, chain)
1901590Srgrimes		p->ni_seen = 0;
1911590Srgrimes	if (protos&TCP) {
1928874Srgrimes		off = NPTR(X_TCB);
1931590Srgrimes		istcp = 1;
1941590Srgrimes	}
1951590Srgrimes	else if (protos&UDP) {
1968874Srgrimes		off = NPTR(X_UDB);
1971590Srgrimes		istcp = 0;
1981590Srgrimes	}
1991590Srgrimes	else {
2001590Srgrimes		error("No protocols to display");
2011590Srgrimes		return;
2021590Srgrimes	}
2031590Srgrimesagain:
2047715Sdg	KREAD(off, &head, sizeof (struct inpcbhead));
20570523Sphk	LIST_FOREACH(next, &head, inp_list) {
2061590Srgrimes		KREAD(next, &inpcb, sizeof (inpcb));
20770526Sphk		next = &inpcb;
2081590Srgrimes		if (!aflag && inet_lnaof(inpcb.inp_laddr) == INADDR_ANY)
2091590Srgrimes			continue;
2101590Srgrimes		if (nhosts && !checkhost(&inpcb))
2111590Srgrimes			continue;
2121590Srgrimes		if (nports && !checkport(&inpcb))
2131590Srgrimes			continue;
2141590Srgrimes		if (istcp) {
215123800Ssilby			if (inpcb.inp_vflag & INP_TIMEWAIT) {
216123800Ssilby				bzero(&sockb, sizeof(sockb));
217123800Ssilby				enter_kvm(&inpcb, &sockb, TCPS_TIME_WAIT,
218123800Ssilby					 "tcp");
219123800Ssilby			} else {
220123800Ssilby				KREAD(inpcb.inp_socket, &sockb,
221123800Ssilby					sizeof (sockb));
222123800Ssilby				KREAD(inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb));
223123800Ssilby				enter_kvm(&inpcb, &sockb, tcpcb.t_state,
224123800Ssilby					"tcp");
225123800Ssilby			}
2261590Srgrimes		} else
22774671Stmm			enter_kvm(&inpcb, &sockb, 0, "udp");
2281590Srgrimes	}
2291590Srgrimes	if (istcp && (protos&UDP)) {
2301590Srgrimes		istcp = 0;
2311590Srgrimes		off = NPTR(X_UDB);
2321590Srgrimes		goto again;
2331590Srgrimes	}
2341590Srgrimes}
2351590Srgrimes
2361590Srgrimesstatic void
23774671Stmmfetchnetstat_sysctl()
23874671Stmm{
23987715Smarkm	struct netinfo *p;
24074671Stmm	int idx;
24174671Stmm	struct xinpgen *inpg;
24274671Stmm	char *cur, *end;
24374671Stmm	struct inpcb *inpcb;
24474671Stmm	struct xinpcb *xip;
24574671Stmm	struct xtcpcb *xtp;
24674671Stmm	int plen;
24774671Stmm	size_t lsz;
24874671Stmm
24974671Stmm	TAILQ_FOREACH(p, &netcb, chain)
25074671Stmm		p->ni_seen = 0;
25174671Stmm	if (protos&TCP) {
25274671Stmm		idx = 0;
25374671Stmm	} else if (protos&UDP) {
25474671Stmm		idx = 1;
25574671Stmm	} else {
25674671Stmm		error("No protocols to display");
25774671Stmm		return;
25874671Stmm	}
25974671Stmm
26074671Stmm	for (;idx < 2; idx++) {
26174671Stmm		if (idx == 1 && !(protos&UDP))
26274671Stmm			break;
26374671Stmm		inpg = (struct xinpgen *)sysctl_dynread(miblist[idx], &lsz);
26474671Stmm		if (inpg == NULL) {
26574671Stmm			error("sysctl(%s...) failed", miblist[idx]);
26674671Stmm			continue;
26774671Stmm		}
26874671Stmm		/*
26974671Stmm		 * We currently do no require a consistent pcb list.
27074671Stmm		 * Try to be robust in case of struct size changes
27174671Stmm		 */
27274671Stmm		cur = ((char *)inpg) + inpg->xig_len;
27374671Stmm		/* There is also a trailing struct xinpgen */
27474671Stmm		end = ((char *)inpg) + lsz - inpg->xig_len;
27574671Stmm		if (end <= cur) {
27674671Stmm			free(inpg);
27774671Stmm			continue;
27874671Stmm		}
27974671Stmm		if (idx == 0) { /* TCP */
28074671Stmm			xtp = (struct xtcpcb *)cur;
28174671Stmm			plen = xtp->xt_len;
28274671Stmm		} else {
28374671Stmm			xip = (struct xinpcb *)cur;
28474671Stmm			plen = xip->xi_len;
28574671Stmm		}
28674671Stmm		while (cur + plen <= end) {
28774671Stmm			if (idx == 0) { /* TCP */
28874671Stmm				xtp = (struct xtcpcb *)cur;
28974671Stmm				inpcb = &xtp->xt_inp;
29074671Stmm			} else {
29174671Stmm				xip = (struct xinpcb *)cur;
29274671Stmm				inpcb = &xip->xi_inp;
29374671Stmm			}
29474671Stmm			cur += plen;
29574671Stmm
29674671Stmm			if (!aflag && inet_lnaof(inpcb->inp_laddr) ==
29774671Stmm			    INADDR_ANY)
29874671Stmm				continue;
29974671Stmm			if (nhosts && !checkhost(inpcb))
30074671Stmm				continue;
30174671Stmm			if (nports && !checkport(inpcb))
30274671Stmm				continue;
30374671Stmm			if (idx == 0)	/* TCP */
30474671Stmm				enter_sysctl(inpcb, &xtp->xt_socket,
30574671Stmm				    xtp->xt_tp.t_state, "tcp");
30674671Stmm			else		/* UDP */
30774671Stmm				enter_sysctl(inpcb, &xip->xi_socket, 0, "udp");
30874671Stmm		}
30974671Stmm		free(inpg);
31074671Stmm	}
31174671Stmm}
31274671Stmm
31374671Stmmstatic void
31474671Stmmenter_kvm(inp, so, state, proto)
31587715Smarkm	struct inpcb *inp;
31687715Smarkm	struct socket *so;
3171590Srgrimes	int state;
31887715Smarkm	const char *proto;
3191590Srgrimes{
32087715Smarkm	struct netinfo *p;
3211590Srgrimes
32274671Stmm	if ((p = enter(inp, state, proto)) != NULL) {
32374671Stmm		p->ni_rcvcc = so->so_rcv.sb_cc;
32474671Stmm		p->ni_sndcc = so->so_snd.sb_cc;
32574671Stmm	}
32674671Stmm}
32774671Stmm
32874671Stmmstatic void
32974671Stmmenter_sysctl(inp, so, state, proto)
33087715Smarkm	struct inpcb *inp;
33187715Smarkm	struct xsocket *so;
33274671Stmm	int state;
33387715Smarkm	const char *proto;
33474671Stmm{
33587715Smarkm	struct netinfo *p;
33674671Stmm
33774671Stmm	if ((p = enter(inp, state, proto)) != NULL) {
33874671Stmm		p->ni_rcvcc = so->so_rcv.sb_cc;
33974671Stmm		p->ni_sndcc = so->so_snd.sb_cc;
34074671Stmm	}
34174671Stmm}
34274671Stmm
34374671Stmm
34474671Stmmstatic struct netinfo *
34574671Stmmenter(inp, state, proto)
34687715Smarkm	struct inpcb *inp;
34774671Stmm	int state;
34887715Smarkm	const char *proto;
34974671Stmm{
35087715Smarkm	struct netinfo *p;
35174671Stmm
3521590Srgrimes	/*
3531590Srgrimes	 * Only take exact matches, any sockets with
3541590Srgrimes	 * previously unbound addresses will be deleted
3551590Srgrimes	 * below in the display routine because they
3561590Srgrimes	 * will appear as ``not seen'' in the kernel
3571590Srgrimes	 * data structures.
3581590Srgrimes	 */
35970523Sphk	TAILQ_FOREACH(p, &netcb, chain) {
3601590Srgrimes		if (!streq(proto, p->ni_proto))
3611590Srgrimes			continue;
3621590Srgrimes		if (p->ni_lport != inp->inp_lport ||
3631590Srgrimes		    p->ni_laddr.s_addr != inp->inp_laddr.s_addr)
3641590Srgrimes			continue;
3651590Srgrimes		if (p->ni_faddr.s_addr == inp->inp_faddr.s_addr &&
3661590Srgrimes		    p->ni_fport == inp->inp_fport)
3671590Srgrimes			break;
3681590Srgrimes	}
36970523Sphk	if (p == NULL) {
3701590Srgrimes		if ((p = malloc(sizeof(*p))) == NULL) {
3711590Srgrimes			error("Out of memory");
37274671Stmm			return NULL;
3731590Srgrimes		}
37470523Sphk		TAILQ_INSERT_HEAD(&netcb, p, chain);
3751590Srgrimes		p->ni_line = -1;
3761590Srgrimes		p->ni_laddr = inp->inp_laddr;
3771590Srgrimes		p->ni_lport = inp->inp_lport;
3781590Srgrimes		p->ni_faddr = inp->inp_faddr;
3791590Srgrimes		p->ni_fport = inp->inp_fport;
38087715Smarkm		p->ni_proto = strdup(proto);
3811590Srgrimes		p->ni_flags = NIF_LACHG|NIF_FACHG;
3821590Srgrimes	}
3831590Srgrimes	p->ni_state = state;
3841590Srgrimes	p->ni_seen = 1;
38574671Stmm	return p;
3861590Srgrimes}
3871590Srgrimes
3881590Srgrimes/* column locations */
3891590Srgrimes#define	LADDR	0
3901590Srgrimes#define	FADDR	LADDR+23
3911590Srgrimes#define	PROTO	FADDR+23
3921590Srgrimes#define	RCVCC	PROTO+6
3931590Srgrimes#define	SNDCC	RCVCC+7
3941590Srgrimes#define	STATE	SNDCC+7
3951590Srgrimes
3961590Srgrimes
3971590Srgrimesvoid
3981590Srgrimeslabelnetstat()
3991590Srgrimes{
40074671Stmm	if (use_kvm && namelist[X_TCB].n_type == 0)
4011590Srgrimes		return;
4021590Srgrimes	wmove(wnd, 0, 0); wclrtobot(wnd);
4031590Srgrimes	mvwaddstr(wnd, 0, LADDR, "Local Address");
4041590Srgrimes	mvwaddstr(wnd, 0, FADDR, "Foreign Address");
4051590Srgrimes	mvwaddstr(wnd, 0, PROTO, "Proto");
4061590Srgrimes	mvwaddstr(wnd, 0, RCVCC, "Recv-Q");
4071590Srgrimes	mvwaddstr(wnd, 0, SNDCC, "Send-Q");
4088874Srgrimes	mvwaddstr(wnd, 0, STATE, "(state)");
4091590Srgrimes}
4101590Srgrimes
4111590Srgrimesvoid
4121590Srgrimesshownetstat()
4131590Srgrimes{
41487715Smarkm	struct netinfo *p, *q;
4151590Srgrimes
4161590Srgrimes	/*
4171590Srgrimes	 * First, delete any connections that have gone
4181590Srgrimes	 * away and adjust the position of connections
4191590Srgrimes	 * below to reflect the deleted line.
4201590Srgrimes	 */
42174671Stmm	p = TAILQ_FIRST(&netcb);
42274671Stmm	while (p != NULL) {
42374671Stmm		if (p->ni_line == -1 || p->ni_seen) {
42474671Stmm			p = TAILQ_NEXT(p, chain);
4251590Srgrimes			continue;
42674671Stmm		}
4271590Srgrimes		wmove(wnd, p->ni_line, 0); wdeleteln(wnd);
42870523Sphk		TAILQ_FOREACH(q, &netcb, chain)
4291590Srgrimes			if (q != p && q->ni_line > p->ni_line) {
4301590Srgrimes				q->ni_line--;
4311590Srgrimes				/* this shouldn't be necessary */
4321590Srgrimes				q->ni_flags |= NIF_LACHG|NIF_FACHG;
4331590Srgrimes			}
4341590Srgrimes		lastrow--;
43574671Stmm		q = TAILQ_NEXT(p, chain);
43670523Sphk		TAILQ_REMOVE(&netcb, p, chain);
4371590Srgrimes		free(p);
4381590Srgrimes		p = q;
4391590Srgrimes	}
4401590Srgrimes	/*
4411590Srgrimes	 * Update existing connections and add new ones.
4421590Srgrimes	 */
44370523Sphk	TAILQ_FOREACH(p, &netcb, chain) {
4441590Srgrimes		if (p->ni_line == -1) {
4451590Srgrimes			/*
4461590Srgrimes			 * Add a new entry if possible.
4471590Srgrimes			 */
4481590Srgrimes			if (lastrow > YMAX(wnd))
4491590Srgrimes				continue;
4501590Srgrimes			p->ni_line = lastrow++;
4511590Srgrimes			p->ni_flags |= NIF_LACHG|NIF_FACHG;
4521590Srgrimes		}
4531590Srgrimes		if (p->ni_flags & NIF_LACHG) {
4541590Srgrimes			wmove(wnd, p->ni_line, LADDR);
4551590Srgrimes			inetprint(&p->ni_laddr, p->ni_lport, p->ni_proto);
4561590Srgrimes			p->ni_flags &= ~NIF_LACHG;
4571590Srgrimes		}
4581590Srgrimes		if (p->ni_flags & NIF_FACHG) {
4591590Srgrimes			wmove(wnd, p->ni_line, FADDR);
4601590Srgrimes			inetprint(&p->ni_faddr, p->ni_fport, p->ni_proto);
4611590Srgrimes			p->ni_flags &= ~NIF_FACHG;
4621590Srgrimes		}
4631590Srgrimes		mvwaddstr(wnd, p->ni_line, PROTO, p->ni_proto);
464100591Sjdp		mvwprintw(wnd, p->ni_line, RCVCC, "%6u", p->ni_rcvcc);
465100591Sjdp		mvwprintw(wnd, p->ni_line, SNDCC, "%6u", p->ni_sndcc);
46687715Smarkm		if (streq(p->ni_proto, "tcp")) {
4671590Srgrimes			if (p->ni_state < 0 || p->ni_state >= TCP_NSTATES)
4681590Srgrimes				mvwprintw(wnd, p->ni_line, STATE, "%d",
4691590Srgrimes				    p->ni_state);
4701590Srgrimes			else
4711590Srgrimes				mvwaddstr(wnd, p->ni_line, STATE,
4721590Srgrimes				    tcpstates[p->ni_state]);
47387715Smarkm		}
4741590Srgrimes		wclrtoeol(wnd);
4751590Srgrimes	}
4761590Srgrimes	if (lastrow < YMAX(wnd)) {
4771590Srgrimes		wmove(wnd, lastrow, 0); wclrtobot(wnd);
4781590Srgrimes		wmove(wnd, YMAX(wnd), 0); wdeleteln(wnd);	/* XXX */
4791590Srgrimes	}
4801590Srgrimes}
4811590Srgrimes
4821590Srgrimes/*
4831590Srgrimes * Pretty print an Internet address (net address + port).
4841590Srgrimes * If the nflag was specified, use numbers instead of names.
4851590Srgrimes */
4861590Srgrimesstatic void
4871590Srgrimesinetprint(in, port, proto)
48887715Smarkm	struct in_addr *in;
4891590Srgrimes	int port;
49087715Smarkm	const char *proto;
4911590Srgrimes{
4921590Srgrimes	struct servent *sp = 0;
49387715Smarkm	char line[80], *cp;
4941590Srgrimes
49536789Simp	snprintf(line, sizeof(line), "%.*s.", 16, inetname(*in));
4961590Srgrimes	cp = index(line, '\0');
4971590Srgrimes	if (!nflag && port)
4981590Srgrimes		sp = getservbyport(port, proto);
4991590Srgrimes	if (sp || port == 0)
50036789Simp		snprintf(cp, sizeof(line) - (cp - line), "%.8s",
50136789Simp		    sp ? sp->s_name : "*");
5021590Srgrimes	else
50336789Simp		snprintf(cp, sizeof(line) - (cp - line), "%d",
50436789Simp		    ntohs((u_short)port));
5051590Srgrimes	/* pad to full column to clear any garbage */
5061590Srgrimes	cp = index(line, '\0');
5071590Srgrimes	while (cp - line < 22)
5081590Srgrimes		*cp++ = ' ';
50919330Sphk	line[22] = '\0';
5101590Srgrimes	waddstr(wnd, line);
5111590Srgrimes}
5121590Srgrimes
5131590Srgrimes/*
5141590Srgrimes * Construct an Internet address representation.
5158874Srgrimes * If the nflag has been supplied, give
5161590Srgrimes * numeric value, otherwise try for symbolic name.
5171590Srgrimes */
5181590Srgrimesstatic char *
5191590Srgrimesinetname(in)
5201590Srgrimes	struct in_addr in;
5211590Srgrimes{
5221590Srgrimes	char *cp = 0;
5231590Srgrimes	static char line[50];
5241590Srgrimes	struct hostent *hp;
5251590Srgrimes	struct netent *np;
5261590Srgrimes
5271590Srgrimes	if (!nflag && in.s_addr != INADDR_ANY) {
5281590Srgrimes		int net = inet_netof(in);
5291590Srgrimes		int lna = inet_lnaof(in);
5301590Srgrimes
5311590Srgrimes		if (lna == INADDR_ANY) {
5321590Srgrimes			np = getnetbyaddr(net, AF_INET);
5331590Srgrimes			if (np)
5341590Srgrimes				cp = np->n_name;
5351590Srgrimes		}
5361590Srgrimes		if (cp == 0) {
5371590Srgrimes			hp = gethostbyaddr((char *)&in, sizeof (in), AF_INET);
5381590Srgrimes			if (hp)
5391590Srgrimes				cp = hp->h_name;
5401590Srgrimes		}
5411590Srgrimes	}
5421590Srgrimes	if (in.s_addr == INADDR_ANY)
5431590Srgrimes		strcpy(line, "*");
5441590Srgrimes	else if (cp)
54536789Simp		snprintf(line, sizeof(line), "%s", cp);
5461590Srgrimes	else {
5471590Srgrimes		in.s_addr = ntohl(in.s_addr);
5481590Srgrimes#define C(x)	((x) & 0xff)
54936789Simp		snprintf(line, sizeof(line), "%u.%u.%u.%u", C(in.s_addr >> 24),
5501590Srgrimes			C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr));
5511590Srgrimes	}
5521590Srgrimes	return (line);
5531590Srgrimes}
5541590Srgrimes
5551590Srgrimesint
5561590Srgrimescmdnetstat(cmd, args)
55787715Smarkm	const char *cmd, *args;
5581590Srgrimes{
5591590Srgrimes	if (prefix(cmd, "all")) {
5601590Srgrimes		aflag = !aflag;
5611590Srgrimes		goto fixup;
5621590Srgrimes	}
5631590Srgrimes	if  (prefix(cmd, "numbers") || prefix(cmd, "names")) {
56470523Sphk		struct netinfo *p;
5651590Srgrimes		int new;
5661590Srgrimes
5671590Srgrimes		new = prefix(cmd, "numbers");
5681590Srgrimes		if (new == nflag)
5691590Srgrimes			return (1);
57070523Sphk		TAILQ_FOREACH(p, &netcb, chain) {
5711590Srgrimes			if (p->ni_line == -1)
5721590Srgrimes				continue;
5731590Srgrimes			p->ni_flags |= NIF_LACHG|NIF_FACHG;
5741590Srgrimes		}
5751590Srgrimes		nflag = new;
5761590Srgrimes		goto redisplay;
5771590Srgrimes	}
5781590Srgrimes	if (!netcmd(cmd, args))
5791590Srgrimes		return (0);
5801590Srgrimesfixup:
5811590Srgrimes	fetchnetstat();
5821590Srgrimesredisplay:
5831590Srgrimes	shownetstat();
5841590Srgrimes	refresh();
5851590Srgrimes	return (1);
5861590Srgrimes}
587