[BACK]Return to netstat.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / systat

File: [local] / src / usr.bin / systat / netstat.c (download)

Revision 1.46, Tue Oct 10 09:27:03 2023 UTC (7 months, 1 week ago) by tb
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, HEAD
Changes since 1.45: +2 -2 lines

Fix a format warning about a non-literal string

ok claudio sthen

/*	$OpenBSD: netstat.c,v 1.46 2023/10/10 09:27:03 tb Exp $	*/
/*	$NetBSD: netstat.c,v 1.3 1995/06/18 23:53:07 cgd Exp $	*/

/*-
 * Copyright (c) 1980, 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * netstat
 */

#include <kvm.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/socket.h>
#define _KERNEL
#include <sys/file.h>
#undef _KERNEL

#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netinet/tcp_seq.h>
#define TCPSTATES
#include <netinet/tcp_fsm.h>
#include <arpa/inet.h>

#include <netdb.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
#include <nlist.h>
#include <paths.h>
#include "systat.h"
#include "engine.h"

#define	TCP	0x1
#define	UDP	0x2
#define	OTHER	0x4

struct netinfo {
	union {
		struct	in_addr nif_laddr;	/* local address */
		struct	in6_addr nif_laddr6;	/* local address */
	} l;
	union {
		struct	in_addr	nif_faddr;	/* foreign address */
		struct	in6_addr nif_faddr6;	/* foreign address */
	} f;
	long	nif_rcvcc;		/* rcv buffer character count */
	long	nif_sndcc;		/* snd buffer character count */
	short	nif_lport;		/* local port */
	short	nif_fport;		/* foreign port */
	short	nif_state;		/* tcp state */
	short	nif_family;
	short	nif_proto;		/* protocol */
	short	nif_ipproto;
};

#define nif_laddr  l.nif_laddr
#define nif_laddr6 l.nif_laddr6
#define nif_faddr  f.nif_faddr
#define nif_faddr6 f.nif_faddr6

static void enter(struct kinfo_file *);
static int kf_comp(const void *, const void *);
static void inetprint(struct in_addr *, int, char *, field_def *);
static void inet6print(struct in6_addr *, int, char *, field_def *);
static void shownetstat(struct netinfo *p);

void print_ns(void);
int read_ns(void);
int select_ns(void);
int ns_keyboard_callback(int);

#define	streq(a,b)	(strcmp(a,b)==0)

static	int aflag = 0;

#define ADD_ALLOC  1000

int protos;

struct netinfo *netinfos = NULL;
size_t num_ns = 0;
static size_t num_alloc = 0;


field_def fields_ns[] = {
	{"LOCAL ADDRESS", 20, 45, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
	{"FOREIGN ADDRESS", 20, 45, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
	{"PROTO", 4, 9, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
	{"RECV-Q", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
	{"SEND-Q", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
	{"STATE", 5, 11, 6, FLD_ALIGN_LEFT, -1, 0, 0, 0},
};

#define FLD_NS_LOCAL	FIELD_ADDR(fields_ns,0)
#define FLD_NS_FOREIGN	FIELD_ADDR(fields_ns,1)
#define FLD_NS_PROTO	FIELD_ADDR(fields_ns,2)
#define FLD_NS_RECV_Q	FIELD_ADDR(fields_ns,3)
#define FLD_NS_SEND_Q	FIELD_ADDR(fields_ns,4)
#define FLD_NS_STATE	FIELD_ADDR(fields_ns,5)

/* Define views */
field_def *view_ns_0[] = {
	FLD_NS_LOCAL, FLD_NS_FOREIGN, FLD_NS_PROTO,
	FLD_NS_RECV_Q, FLD_NS_SEND_Q, FLD_NS_STATE, NULL
};

/* Define view managers */
struct view_manager netstat_mgr = {
	"Netstat", select_ns, read_ns, NULL, print_header,
	print_ns, ns_keyboard_callback, NULL, NULL
};

field_view views_ns[] = {
	{view_ns_0, "netstat", '0', &netstat_mgr},
	{NULL, NULL, 0, NULL}
};




struct netinfo *
next_ns(void)
{
	if (num_alloc <= num_ns) {
		struct netinfo *ni;
		size_t a = num_alloc + ADD_ALLOC;
		if (a < num_alloc)
			return NULL;
		ni = reallocarray(netinfos, a, sizeof(*ni));
		if (ni == NULL)
			return NULL;
		netinfos = ni;
		num_alloc = a;
	}

	return &netinfos[num_ns++];
}

static void
enter(struct kinfo_file *kf)
{
#define s6_addr32 __u6_addr.__u6_addr32
	struct netinfo *p;

	/* first filter out unwanted sockets */
	if (kf->so_family != AF_INET && kf->so_family != AF_INET6)
		return;

	switch (kf->so_protocol) {
	case IPPROTO_TCP:
		if ((protos & TCP) == 0)
			return;
		break;
	case IPPROTO_UDP:
		if ((protos & UDP) == 0)
			return;
		break;
	default:
		if ((protos & OTHER) == 0)
			return;
		break;
	}

	if (!aflag) {
		struct in6_addr faddr6;

		switch (kf->so_family) {
		case AF_INET:
			if (kf->inp_faddru[0] == INADDR_ANY)
				return;
			break;
		case AF_INET6:
			faddr6.s6_addr32[0] = kf->inp_faddru[0];
			faddr6.s6_addr32[1] = kf->inp_faddru[1];
			faddr6.s6_addr32[2] = kf->inp_faddru[2];
			faddr6.s6_addr32[3] = kf->inp_faddru[3];
			if (IN6_IS_ADDR_UNSPECIFIED(&faddr6))
				return;
			break;
		}
	}

	/* finally enter the socket to the table */
	p = next_ns();
	if (p == NULL) {
		error("Out of Memory!");
		return;
	}

	p->nif_lport = kf->inp_lport;
	p->nif_fport = kf->inp_fport;
	p->nif_proto = kf->so_protocol;
	p->nif_ipproto = kf->inp_proto;

	switch (kf->so_family) {
	case AF_INET:
		p->nif_family = AF_INET;
		p->nif_laddr.s_addr = kf->inp_laddru[0];
		p->nif_faddr.s_addr = kf->inp_faddru[0];
		break;
	case AF_INET6:
		p->nif_family = AF_INET6;
		p->nif_laddr6.s6_addr32[0] = kf->inp_laddru[0];
		p->nif_laddr6.s6_addr32[1] = kf->inp_laddru[1];
		p->nif_laddr6.s6_addr32[2] = kf->inp_laddru[2];
		p->nif_laddr6.s6_addr32[3] = kf->inp_laddru[3];
		p->nif_faddr6.s6_addr32[0] = kf->inp_faddru[0];
		p->nif_faddr6.s6_addr32[1] = kf->inp_faddru[1];
		p->nif_faddr6.s6_addr32[2] = kf->inp_faddru[2];
		p->nif_faddr6.s6_addr32[3] = kf->inp_faddru[3];
		break;
	}

	p->nif_rcvcc = kf->so_rcv_cc;
	p->nif_sndcc = kf->so_snd_cc;
	p->nif_state = kf->t_state;
#undef s6_addr32
}


/* netstat callback functions */

int
select_ns(void)
{
	num_disp = num_ns;
	return (0);
}

static int type_map[] = { -1, 2, 3, 1, 4, 5 };

static int
kf_comp(const void *a, const void *b)
{
	const struct kinfo_file *ka = a, *kb = b;

	if (ka->so_family != kb->so_family) {
		/* AF_INET < AF_INET6 < AF_LOCAL */
		if (ka->so_family == AF_INET)
			return (-1);
		if (ka->so_family == AF_LOCAL)
			return (1);
		if (kb->so_family == AF_LOCAL)
			return (-1);
		return (1);
	}
	if (ka->so_family == AF_LOCAL) {
		if (type_map[ka->so_type] < type_map[kb->so_type])
			return (-1);
		if (type_map[ka->so_type] > type_map[kb->so_type])
			return (1);
	} else if (ka->so_family == AF_INET || ka->so_family == AF_INET6) {
		if (ka->so_protocol < kb->so_protocol)
			return (-1);
		if (ka->so_protocol > kb->so_protocol)
			return (1);
		if (ka->so_type == SOCK_DGRAM || ka->so_type == SOCK_STREAM) {
			/* order sockets by remote port desc */
			if (ka->inp_fport > kb->inp_fport)
				return (-1);
			if (ka->inp_fport < kb->inp_fport)
				return (1);
		} else if (ka->so_type == SOCK_RAW) {
			if (ka->inp_proto > kb->inp_proto)
				return (-1);
			if (ka->inp_proto < kb->inp_proto)
				return (1);
		}
	}
	return (0);
}


int
read_ns(void)
{
	struct kinfo_file *kf;
	int i, fcnt;

	if (kd == NULL) {
		error("Failed to initialize KVM!");
		return (0);
	}
	kf = kvm_getfiles(kd, KERN_FILE_BYFILE, DTYPE_SOCKET,
	    sizeof(*kf), &fcnt);
	if (kf == NULL) {
		error("Out of Memory!");
		return (0);
	}

	/* sort sockets by AF, proto and type */
	qsort(kf, fcnt, sizeof(*kf), kf_comp);

	num_ns = 0;

	for (i = 0; i < fcnt; i++)
		enter(&kf[i]);

	num_disp = num_ns;
	return 0;
}

void
print_ns(void)
{
	int n, count = 0;

	for (n = dispstart; n < num_disp; n++) {
		shownetstat(netinfos + n);
		count++;
		if (maxprint > 0 && count >= maxprint)
			break;
	}
}


int
initnetstat(void)
{
	field_view *v;

	protos = TCP|UDP|OTHER;
	for (v = views_ns; v->name != NULL; v++)
		add_view(v);

	return(1);
}

static void
shownetstat(struct netinfo *p)
{
	char *proto = NULL;

	switch (p->nif_proto) {
	case IPPROTO_TCP:
		proto = "tcp";
		break;
	case IPPROTO_UDP:
		proto = "udp";
		break;
	}

	switch (p->nif_family) {
	case AF_INET:
		inetprint(&p->nif_laddr, p->nif_lport,
			  proto, FLD_NS_LOCAL);
		inetprint(&p->nif_faddr, p->nif_fport,
			  proto, FLD_NS_FOREIGN);
		break;
	case AF_INET6:
		inet6print(&p->nif_laddr6, p->nif_lport,
			   proto, FLD_NS_LOCAL);
		inet6print(&p->nif_faddr6, p->nif_fport,
			   proto, FLD_NS_FOREIGN);
		break;
	}
 
	tb_start();
	switch (p->nif_proto) {
	case IPPROTO_TCP:
	case IPPROTO_UDP:
		tbprintf("%s", proto);
		if (p->nif_family == AF_INET6)
			tbprintf("6");
		break;
	case IPPROTO_DIVERT:
		tbprintf("divert");
		if (p->nif_family == AF_INET6)
			tbprintf("6");
		break;
	default:
		tbprintf("%d", p->nif_ipproto);
		break;
	}

	print_fld_tb(FLD_NS_PROTO);

	print_fld_size(FLD_NS_RECV_Q, p->nif_rcvcc);
	print_fld_size(FLD_NS_SEND_Q, p->nif_sndcc);

	if (p->nif_proto == IPPROTO_TCP) {
		if (p->nif_state < 0 || p->nif_state >= TCP_NSTATES)
			print_fld_uint(FLD_NS_STATE, p->nif_state);
		else
			print_fld_str(FLD_NS_STATE, tcpstates[p->nif_state]);
	}
	end_line();
}

/*
 * Pretty print an Internet address (net address + port).
 * If the nflag was specified, use numbers instead of names.
 */
static void
inetprint(struct in_addr *in, int port, char *proto, field_def *fld)
{
	struct servent *sp = 0;

	tb_start();
	tbprintf("%s", inetname(*in));

	if (!nflag && port)
		sp = getservbyport(port, proto);
	if (sp || port == 0)
		tbprintf(":%s", sp ? sp->s_name : "*");
	else
		tbprintf(":%d", ntohs((u_short)port));

	print_fld_tb(fld);
}

static void
inet6print(struct in6_addr *in6, int port, char *proto, field_def *fld)
{
	struct servent *sp = 0;

	tb_start();

	tbprintf("%s", inet6name(in6));
	if (!nflag && port)
		sp = getservbyport(port, proto);
	if (sp || port == 0)
		tbprintf(":%s", sp ? sp->s_name : "*");
	else
		tbprintf(":%d", ntohs((u_short)port));

	print_fld_tb(fld);
}

int
ns_keyboard_callback(int ch)
{
	switch (ch) {
	case 'a':
		aflag = !aflag;
		gotsig_alarm = 1;
		break;
	case 'n':
		nflag = !nflag;
		gotsig_alarm = 1;
		break;
	case 'o':
		protos ^= OTHER;
		gotsig_alarm = 1;
		break;
	case 'r':
		aflag = 0;
		nflag = 1;
		protos = TCP|UDP;
		gotsig_alarm = 1;
		break;
	case 't':
		protos ^= TCP;
		gotsig_alarm = 1;
		break;
	case 'u':
		protos ^= UDP;
		gotsig_alarm = 1;
		break;
	default:
		return keyboard_callback(ch);
	};

	return 1;
}